OSDN Git Service

Add a flag to enable/disable the diskstats logging service.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / storage / DiskStatsLoggingService.java
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16
17 package com.android.server.storage;
18
19 import android.app.job.JobInfo;
20 import android.app.job.JobParameters;
21 import android.app.job.JobScheduler;
22 import android.app.job.JobService;
23 import android.content.ComponentName;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.pm.PackageStats;
27 import android.os.AsyncTask;
28 import android.os.BatteryManager;
29 import android.os.Environment;
30 import android.os.Environment.UserEnvironment;
31 import android.os.UserHandle;
32 import android.provider.Settings;
33 import android.util.Log;
34
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.server.storage.FileCollector.MeasurementResult;
37
38 import java.io.File;
39 import java.io.IOException;
40 import java.util.List;
41 import java.util.concurrent.TimeUnit;
42
43 /**
44  * DiskStatsLoggingService is a JobService which collects storage categorization information and
45  * app size information on a roughly daily cadence.
46  */
47 public class DiskStatsLoggingService extends JobService {
48     private static final String TAG = "DiskStatsLogService";
49     public static final String DUMPSYS_CACHE_PATH = "/data/system/diskstats_cache.json";
50     private static final int JOB_DISKSTATS_LOGGING = 0x4449534b; // DISK
51     private static ComponentName sDiskStatsLoggingService = new ComponentName(
52             "android",
53             DiskStatsLoggingService.class.getName());
54
55     @Override
56     public boolean onStartJob(JobParameters params) {
57         // We need to check the preconditions again because they may not be enforced for
58         // subsequent runs.
59         if (!isCharging(this) || !isDumpsysTaskEnabled(getContentResolver())) {
60             jobFinished(params, true);
61             return false;
62         }
63
64         final int userId = UserHandle.myUserId();
65         UserEnvironment environment = new UserEnvironment(userId);
66         AppCollector collector = new AppCollector(this,
67                 getPackageManager().getPrimaryStorageCurrentVolume());
68         LogRunnable task = new LogRunnable();
69         task.setRootDirectory(environment.getExternalStorageDirectory());
70         task.setDownloadsDirectory(
71                 environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
72         task.setSystemSize(FileCollector.getSystemSize(this));
73         task.setLogOutputFile(new File(DUMPSYS_CACHE_PATH));
74         task.setAppCollector(collector);
75         task.setJobService(this, params);
76         AsyncTask.execute(task);
77         return true;
78     }
79
80     @Override
81     public boolean onStopJob(JobParameters params) {
82         // TODO: Try to stop being handled.
83         return false;
84     }
85
86     /**
87      * Schedules a DiskStats collection task. This task only runs on device idle while charging
88      * once every 24 hours.
89      * @param context Context to use to get a job scheduler.
90      */
91     public static void schedule(Context context) {
92         JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
93
94         js.schedule(new JobInfo.Builder(JOB_DISKSTATS_LOGGING, sDiskStatsLoggingService)
95                 .setRequiresDeviceIdle(true)
96                 .setRequiresCharging(true)
97                 .setPeriodic(TimeUnit.DAYS.toMillis(1))
98                 .build());
99     }
100
101     private static boolean isCharging(Context context) {
102         BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
103         if (batteryManager != null) {
104             return batteryManager.isCharging();
105         }
106         return false;
107     }
108
109     @VisibleForTesting
110     static boolean isDumpsysTaskEnabled(ContentResolver resolver) {
111         // The default is to treat the task as enabled.
112         return Settings.Global.getInt(resolver, Settings.Global.ENABLE_DISKSTATS_LOGGING, 1) != 0;
113     }
114
115     @VisibleForTesting
116     static class LogRunnable implements Runnable {
117         private static final long TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
118
119         private JobService mJobService;
120         private JobParameters mParams;
121         private AppCollector mCollector;
122         private File mOutputFile;
123         private File mRootDirectory;
124         private File mDownloadsDirectory;
125         private long mSystemSize;
126
127         public void setRootDirectory(File file) {
128             mRootDirectory = file;
129         }
130
131         public void setDownloadsDirectory(File file) {
132             mDownloadsDirectory = file;
133         }
134
135         public void setAppCollector(AppCollector collector) {
136             mCollector = collector;
137         }
138
139         public void setLogOutputFile(File file) {
140             mOutputFile = file;
141         }
142
143         public void setSystemSize(long size) {
144             mSystemSize = size;
145         }
146
147         public void setJobService(JobService jobService, JobParameters params) {
148             mJobService = jobService;
149             mParams = params;
150         }
151
152         public void run() {
153             FileCollector.MeasurementResult mainCategories =
154                     FileCollector.getMeasurementResult(mRootDirectory);
155             FileCollector.MeasurementResult downloads =
156                     FileCollector.getMeasurementResult(mDownloadsDirectory);
157
158             boolean needsReschedule = true;
159             List<PackageStats> stats = mCollector.getPackageStats(TIMEOUT_MILLIS);
160             if (stats != null) {
161                 needsReschedule = false;
162                 logToFile(mainCategories, downloads, stats, mSystemSize);
163             } else {
164                 Log.w("TAG", "Timed out while fetching package stats.");
165             }
166
167             if (mJobService != null) {
168                 mJobService.jobFinished(mParams, needsReschedule);
169             }
170         }
171
172         private void logToFile(MeasurementResult mainCategories, MeasurementResult downloads,
173                 List<PackageStats> stats, long systemSize) {
174             DiskStatsFileLogger logger = new DiskStatsFileLogger(mainCategories, downloads, stats,
175                     systemSize);
176             try {
177                 mOutputFile.createNewFile();
178                 logger.dumpToFile(mOutputFile);
179             } catch (IOException e) {
180                 Log.e(TAG, "Exception while writing opportunistic disk file cache.", e);
181             }
182         }
183     }
184 }