UsageStatsManager write data

Interface init

ActivityManagerService

public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
    mUsageStatsService = usageStatsManager;
}

SystemServer

It is invoked in SystemServer:

private void startCoreServices() {
    // Tracks the battery level.  Requires LightService.
    mSystemServiceManager.startService(BatteryService.class);

    // Tracks application usage stats.
    mSystemServiceManager.startService(UsageStatsService.class);
    mActivityManagerService.setUsageStatsManager(
            LocalServices.getService(UsageStatsManagerInternal.class));

    // Tracks whether the updatable WebView is in a ready state and watches for update installs.
    mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
}

ActivityManagerService will invoke updateUsageStats
indirectly in UsageStatsService class to update usage data.

UsageStatsService updateUsageStats

void updateUsageStats(ActivityRecord component, boolean resumed) {
    if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
            "updateUsageStats: comp=" + component + "res=" + resumed);
    final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
    if (resumed) {
        if (mUsageStatsService != null) {
            mUsageStatsService.reportEvent(component.realActivity, component.userId,
                    UsageEvents.Event.MOVE_TO_FOREGROUND);
        }
        synchronized (stats) {
            stats.noteActivityResumedLocked(component.app.uid);
        }
    } else {
        if (mUsageStatsService != null) {
            mUsageStatsService.reportEvent(component.realActivity, component.userId,
                    UsageEvents.Event.MOVE_TO_BACKGROUND);
        }
        synchronized (stats) {
            stats.noteActivityPausedLocked(component.app.uid);
        }
    }
}

it is invoked in ActivityStack startPausingLocked with three points:

ActivityStack startPausingLocked

when a activity is launched,it will be added to stack,from ActivityManager to ActivityManagerService,it invokes ActivityStackSupervisor createStackOnDisplay method,in this method, it initializes ActivityContainer object,and allocations a stack id for this activity,in ActivityContainer constructor,it initializes Stack object.

ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
    ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
    if (activityDisplay == null) {
        return null;
    }

    ActivityContainer activityContainer = new ActivityContainer(stackId);
    mActivityContainers.put(stackId, activityContainer);
    activityContainer.attachToDisplayLocked(activityDisplay, onTop);
    return activityContainer.mStack;
}

in ActivityContainer:

ActivityContainer(int stackId) {
    synchronized (mService) {
        mStackId = stackId;
        mStack = new ActivityStack(this, mRecentTasks);
        mIdString = "ActivtyContainer{" + mStackId + "}";
        if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this);
    }
}

When a activity from resume-state to pause-state,it may invoke startPausingLocked in ActivityStack class,this method is invoked in several conditions:

  • boot is finishing but not unlock
  • device is locked and lock view is visible
  • device is locked and lock view is hidden while a activity at the top of stack meanwhile device is in dreaming or doze or awake mode which indicate device is sleeping
  • activity is popping out and finished
  • app’s process died but exists visible activity
  • activity at top while device unlocked
  • activity configuration is changed
  • activity from or to Translucent
  • new activity starts while others to pause state

the sleep which we can find in AMS:

private boolean shouldSleepLocked() {
    // Resume applications while running a voice interactor.
    if (mRunningVoice != null) {
        return false;
    }

    // TODO: Transform the lock screen state into a sleep token instead.
    switch (mWakefulness) {
        case PowerManagerInternal.WAKEFULNESS_AWAKE:
        case PowerManagerInternal.WAKEFULNESS_DREAMING:
        case PowerManagerInternal.WAKEFULNESS_DOZING:
            // Pause applications whenever the lock screen is shown or any sleep
            // tokens have been acquired.
            return (mLockScreenShown != LOCK_SCREEN_HIDDEN || !mSleepTokens.isEmpty());
        case PowerManagerInternal.WAKEFULNESS_ASLEEP:
        default:
            // If we're asleep then pause applications unconditionally.
            return true;
    }
}
  • shutdown
@Override
public boolean shutdown(int timeout) {
    if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
            != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("Requires permission "
                + android.Manifest.permission.SHUTDOWN);
    }

    boolean timedout = false;

    synchronized(this) {
        mShuttingDown = true;
        updateEventDispatchingLocked();
        timedout = mStackSupervisor.shutdownLocked(timeout);
    }

    mAppOpsService.shutdown();

    if (mUsageStatsService != null) {
        mUsageStatsService.prepareShutdown();
    }
    mBatteryStatsService.shutdown();
    synchronized (this) {
        mProcessStats.shutdownLocked();
        notifyTaskPersisterLocked(null, true);
    }

    return timedout;
}

After SHUT_DOWN broadcast is sent,IActivityManager will invoke shutdown method,this referred to shutdown in AMS.

  • maybeUpdateUsageStatsLocked
private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
   if (DEBUG_USAGE_STATS) {
       Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
               + "] state changes: old = " + app.setProcState + ", new = "
               + app.curProcState);
   }
   if (mUsageStatsService == null) {
       return;
   }
   boolean isInteraction;
   // To avoid some abuse patterns, we are going to be careful about what we consider
   // to be an app interaction.  Being the top activity doesn't count while the display
   // is sleeping, nor do short foreground services.
   if (app.curProcState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
       isInteraction = true;
       app.fgInteractionTime = 0;
   } else if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
       if (app.fgInteractionTime == 0) {
           app.fgInteractionTime = nowElapsed;
           isInteraction = false;
       } else {
           isInteraction = nowElapsed > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME;
       }
   } else {
       // If the app was being forced to the foreground, by say a Toast, then
       // no need to treat it as an interaction
       isInteraction = app.forcingToForeground == null
               && app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
       app.fgInteractionTime = 0;
   }
   if (isInteraction && (!app.reportedInteraction
           || (nowElapsed-app.interactionEventTime) > USAGE_STATS_INTERACTION_INTERVAL)) {
       app.interactionEventTime = nowElapsed;
       String[] packages = app.getPackageList();
       if (packages != null) {
           for (int i = 0; i < packages.length; i++) {
               mUsageStatsService.reportEvent(packages[i], app.userId,
                       UsageEvents.Event.SYSTEM_INTERACTION);
           }
       }
   }
   app.reportedInteraction = isInteraction;
   if (!isInteraction) {
       app.interactionEventTime = 0;
   }
}

The method is invoked in applyOomAdjLocked method.adj is a strategy by goals for app,it relates to each app’s memory,and can be triggered with activity starting or finishing,service starting or binding or destroying and so on.It also shows that maximum time for updating usage stats is 24 hours.
However,the applyOomAdjLocked invokes UsageStatsService.reportEvent with UsageEvents.Event.SYSTEM_INTERACTION argument,we will analysis later.
Backing to updateUsageStats method,we can find three core codes update usage stats:

mUsageStatsService.reportEvent(component.realActivity, component.userId,UsageEvents.Event.MOVE_TO_FOREGROUND);
mUsageStatsService.reportEvent(component.realActivity, component.userId,UsageEvents.Event.MOVE_TO_BACKGROUND);
mUsageStatsService.reportEvent(packages[i], app.userId,UsageEvents.Event.SYSTEM_INTERACTION);

UsageStatsManagerInternal

  • UsageStatsService LocalService

In UsageStatsService class,we can find that LocalService extends UsageStatsManagerInternal,reportEvent method defines here:

private final class LocalService extends UsageStatsManagerInternal {
    @Override
    public void reportEvent(ComponentName component, int userId, int eventType) {
        if (component == null) {
            Slog.w(TAG, "Event reported without a component name");
            return;
        }

        UsageEvents.Event event = new UsageEvents.Event();
        event.mPackage = component.getPackageName();
        event.mClass = component.getClassName();

        // This will later be converted to system time.
        event.mTimeStamp = SystemClock.elapsedRealtime();

        event.mEventType = eventType;
        mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
    }

    @Override
    public void reportEvent(String packageName, int userId, int eventType) {
        if (packageName == null) {
            Slog.w(TAG, "Event reported without a package name");
            return;
        }

        UsageEvents.Event event = new UsageEvents.Event();
        event.mPackage = packageName;

        // This will later be converted to system time.
        event.mTimeStamp = SystemClock.elapsedRealtime();

        event.mEventType = eventType;
        mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
    }
}
  • UserUsageStatsService reportEvent

the actual work is in a handler which finally invoke reportEvent in UserUsageStatsService class:

void reportEvent(UsageEvents.Event event) {
    if (DEBUG) {
        Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
                + "[" + event.mTimeStamp + "]: "
                + eventToString(event.mEventType));
    }

    if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
        // Need to rollover
        rolloverStats(event.mTimeStamp);
    }

    final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];

    final Configuration newFullConfig = event.mConfiguration;
    if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
            currentDailyStats.activeConfiguration != null) {
        // Make the event configuration a delta.
        event.mConfiguration = Configuration.generateDelta(
                currentDailyStats.activeConfiguration, newFullConfig);
    }

    // Add the event to the daily list.
    if (currentDailyStats.events == null) {
        currentDailyStats.events = new TimeSparseArray<>();
    }
    if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) {
        currentDailyStats.events.put(event.mTimeStamp, event);
    }

    for (IntervalStats stats : mCurrentStats) {
        if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
            stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
        } else {
            stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
        }
    }

    notifyStatsChanged();
}

we can figure out that the latest usage event is put into mCurrentStats events object with INTERVAL_DAILY type.

currentDailyStats.events.put(event.mTimeStamp, event);

And core code is:

stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
  • IntervalStats update

In IntervalStats update method:

void update(String packageName, long timeStamp, int eventType) {
    UsageStats usageStats = getOrCreateUsageStats(packageName);

    // TODO(adamlesinski): Ensure that we recover from incorrect event sequences
    // like double MOVE_TO_BACKGROUND, etc.
    if (eventType == UsageEvents.Event.MOVE_TO_BACKGROUND ||
            eventType == UsageEvents.Event.END_OF_DAY) {
        if (usageStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
                usageStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
            usageStats.mTotalTimeInForeground += timeStamp - usageStats.mLastTimeUsed;
        }
    }

    if (isStatefulEvent(eventType)) {
        usageStats.mLastEvent = eventType;
    }

    if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) {
        usageStats.mLastTimeUsed = timeStamp;
    }
    usageStats.mEndTimeStamp = timeStamp;
    endTime = timeStamp;
}

get the last usage stats with same package name or create a new one:

UsageStats usageStats = getOrCreateUsageStats(packageName);

getOrCreateUsageStats method:

UsageStats getOrCreateUsageStats(String packageName) {
    UsageStats usageStats = packageStats.get(packageName);
    if (usageStats == null) {
        usageStats = new UsageStats();
        usageStats.mPackageName = getCachedStringRef(packageName);
        usageStats.mBeginTimeStamp = beginTime;
        usageStats.mEndTimeStamp = endTime;
        packageStats.put(usageStats.mPackageName, usageStats);
    }
    return usageStats;
}

beginTime is initialized in init() method in UserUsageStatsService class which contains loadActiveStats method and begin-Time is the initial-time stamp when UserUsageStatsService is first initialized for user.
and current usage stats object is saved in:

public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
  • notifyStatsChanged()

updating usage stats is doing well and notifyStatsChanged(); is emerging at the last of reportEvent method:

private void notifyStatsChanged() {
    if (!mStatsChanged) {
        mStatsChanged = true;
        mListener.onStatsUpdated();
    }
}
  • UsageStatsService onStatsUpdated

mListener is from UsageStatsService,so onStatsUpdated will be invoked in UsageStatsService class:

public void onStatsUpdated() {
    mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL);
}
  • UsageStatsService flushToDiskLocked

Returned to handler again,this message invoke service.persistActiveStats:

private void flushToDiskLocked() {
    final int userCount = mUserState.size();
    for (int i = 0; i < userCount; i++) {
        UserUsageStatsService service = mUserState.valueAt(i);
        service.persistActiveStats();
        mAppIdleHistory.writeAppIdleTimesLocked(mUserState.keyAt(i));
    }
    // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
    // considered not-idle, which is the safest outcome in such an event.
    mAppIdleHistory.writeAppIdleDurationsLocked();
    mHandler.removeMessages(MSG_FLUSH_TO_DISK);
}
  • UserUsageStatsService persistActiveStats

service is UserUsageStatsService,method persistActiveStats is:

void persistActiveStats() {
    if (mStatsChanged) {
        Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
        try {
            for (int i = 0; i < mCurrentStats.length; i++) {
                mDatabase.putUsageStats(i, mCurrentStats[i]);
            }
            mStatsChanged = false;
        } catch (IOException e) {
            Slog.e(TAG, mLogPrefix + "Failed to persist active stats", e);
        }
    }
}
  • UsageStatsDatabase putUsageStats

well,invoked in putUsageStats in UsageStatsDatabase class,and data will be written in each interval type file unless data is exist:

public void putUsageStats(int intervalType, IntervalStats stats) throws IOException {
    if (stats == null) return;
    synchronized (mLock) {
        if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
            throw new IllegalArgumentException("Bad interval type " + intervalType);
        }

        AtomicFile f = mSortedStatFiles[intervalType].get(stats.beginTime);
        if (f == null) {
            f = new AtomicFile(new File(mIntervalDirs[intervalType],
                    Long.toString(stats.beginTime)));
            mSortedStatFiles[intervalType].put(stats.beginTime, f);
        }

        UsageStatsXml.write(f, stats);
        stats.lastTimeSaved = f.getLastModifiedTime();
    }
}
  • UsageStatsXMLV1 write

finally,the UsageStatsXMLV1class write method is invoked,the core codes:

for (int i = 0; i < statsCount; i++) {
    writeUsageStats(xml, stats, stats.packageStats.valueAt(i));
}
  • UsageStatsXMLV1 writeUsageStats

writeUsageStats method:

private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
        final UsageStats usageStats) throws IOException {
    xml.startTag(null, PACKAGE_TAG);

    // Write the time offset.
    XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
            usageStats.mLastTimeUsed - stats.beginTime);

    XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
    XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
    XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
    xml.endTag(null, PACKAGE_TAG);
}

it will write package name,last usage time,usage duration and last event in file.

usage duration
usageStats.mTotalTimeInForeground += timeStamp - usageStats.mLastTimeUsed;

timeStamp is current time,mLastTimeUsed is last update time.

end time equals to current update-time
endTime = timeStamp;

and

usageStats.mEndTimeStamp = timeStamp;
LAST_TIME_ACTIVE_ATTR = “lastTimeActive”;

it equals to usageStats.mLastTimeUsed - stats.beginTime.
it is not the package or component start time or last usage time,but is the gap which is between last update time and UsageUsageStatsService initial time.

SystemServer ActivityMan ActivityStack UsageStatsMa LocalService UsageStatsService UserUsageS IntervalStats StatsUpdat UsageStat UsageStatsXml UsageStatsXmlV1 XmlUtils startCoreServices setUsageStatsManager with UsageStatsManagerInternal LocalService in UsageStatsService extends UsageStatsManagerInternal invoke startPausingLocked with several conditions startPausingLocked contains updateUsageStats reportEvent invoke LocalService class in UsageStatsService Handler send MSG_REPORT_EVENT message to reportEvent reportEvent invoke and put UsageEvents.Event in INTERVAL_DAILY of IntervalStats update invoke,calculate data and put data in packageStats,convert Event to IntervalStats notifyStatsChanged invoke onStatsUpdated invoke with mListener object UsageStatsService implements StatsUpdatedListener and overrided onStatsUpdated invoke Handler send MSG_FLUSH_TO_DISK message flushToDisk invoke flushToDiskLocked invoke persistActiveStats invoke putUsageStats invoke write invoke write invoke with IntervalStats packageStats object writeUsageStats invoke write each attribute to xml file SystemServer ActivityMan ActivityStack UsageStatsMa LocalService UsageStatsService UserUsageS IntervalStats StatsUpdat UsageStat UsageStatsXml UsageStatsXmlV1 XmlUtils UsageStatsService Init
### 关于 `writedata` 函数或方法的作用及用法 在编程领域中,`writedata` 并不是一个标准库函数或广泛使用的术语,因此它的具体含义通常取决于上下文环境和所使用的语言或框架。以下是可能的情况及其解释: #### 1. **文件写入** 在某些情况下,`writedata` 可能是一个自定义函数或方法,用于向文件或其他存储介质写入数据。例如,在 Python 中可以实现类似的逻辑: ```python def writedata(file_path, data): with open(file_path, 'w') as file: file.write(data) ``` 上述代码片段展示了如何通过一个名为 `writedata` 的函数将字符串形式的数据写入指定路径的文件中[^5]。 #### 2. **网络通信中的数据发送** 在网络编程场景下,`writedata` 还可被用来描述一种机制,即将特定格式的数据包发送至远程服务器或客户端。比如基于 TCP 协议的应用层开发中,可能会有如下伪代码: ```c void writedata(int socket_fd, const char *buffer, size_t length) { send(socket_fd, buffer, length, 0); } ``` 这里假设存在一个已建立连接的有效套接字句柄 `socket_fd`,并通过调用系统级 API 完成实际传输过程[^6]。 #### 3. **数据库交互操作** 当涉及到持久化存储时,“write”动作往往指向记录新增/修改行为。“data”则代表待处理的信息实体集合。所以对于关系型数据库管理系统而言,执行 SQL 插入语句亦可视作广义上的“writing”。相应地,ORM(Object Relational Mapping) 工具也可能提供类似命名风格的方法来简化这一流程[^7]。 --- ### 示例代码展示不同情境下的应用方式 #### 文件IO示例(Python版) ```python def writedata(filename, content): try: with open(filename, mode='a', encoding='utf8') as fobj: fobj.writelines(content+'\n') except Exception as e: print(f'Error occurred while writing to {filename}: ', str(e)) # Usage Example sample_text = "This is a test line." target_file = "./output.txt" writedata(target_file, sample_text) ``` #### Socket Data Transmission(C语言版本) ```c #include <sys/socket.h> #include <arpa/inet.h> int writedata(int sockfd, void* buf, int len){ return send(sockfd , buf , len , 0 ); } // Assume we have initialized and connected the socket properly before calling this function. char message[]="GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"; if(writedata(client_socket,message,strlen(message))<0){ perror("Send failed"); close(client_socket); exit(1); } ``` #### Database Record Insertion Using SQLAlchemy(Python ORM Library) ```python from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String) engine = create_engine('sqlite:///example.db') Session = sessionmaker(bind=engine) session = Session() new_user = User(name='John Doe') def writedata(entity_obj): session.add(entity_obj) session.commit() writedata(new_user) ``` --- ### 总结 尽管无法确切得知提问者提及的具体技术栈背景是什么样的情况,但从常规理解角度来看,`writedata` 很大程度上关联着某种形式的数据输出活动——无论是针对本地磁盘资源还是远端服务接口抑或是内部状态维护等方面均有可能涉及此类概念[^8]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值