blob: 4a86175be2dfe41a7ed8e7931863d6df64b5d107 [file] [log] [blame]
Daniel Nishi090b2d92016-12-13 10:38:42 -08001/*
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
17package com.android.server.storage;
18
19import android.app.job.JobInfo;
20import android.app.job.JobParameters;
21import android.app.job.JobScheduler;
22import android.app.job.JobService;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.pm.PackageStats;
26import android.os.AsyncTask;
27import android.os.BatteryManager;
28import android.os.Environment;
29import android.os.Environment.UserEnvironment;
30import android.os.UserHandle;
31import android.util.Log;
32
33import com.android.internal.annotations.VisibleForTesting;
34import com.android.server.storage.FileCollector.MeasurementResult;
35
36import java.io.File;
37import java.io.IOException;
38import java.util.List;
39import java.util.concurrent.TimeUnit;
40
41/**
42 * DiskStatsLoggingService is a JobService which collects storage categorization information and
43 * app size information on a roughly daily cadence.
44 */
45public class DiskStatsLoggingService extends JobService {
46 private static final String TAG = "DiskStatsLogService";
47 public static final String DUMPSYS_CACHE_PATH = "/data/system/diskstats_cache.json";
48 private static final int JOB_DISKSTATS_LOGGING = 0x4449534b; // DISK
49 private static ComponentName sDiskStatsLoggingService = new ComponentName(
50 "android",
51 DiskStatsLoggingService.class.getName());
52
53 @Override
54 public boolean onStartJob(JobParameters params) {
55 // We need to check the preconditions again because they may not be enforced for
56 // subsequent runs.
57 if (!isCharging(this)) {
58 jobFinished(params, true);
59 return false;
60 }
61
62 final int userId = UserHandle.myUserId();
63 UserEnvironment environment = new UserEnvironment(userId);
64 AppCollector collector = new AppCollector(this,
65 getPackageManager().getPrimaryStorageCurrentVolume());
66 LogRunnable task = new LogRunnable();
67 task.setRootDirectory(environment.getExternalStorageDirectory());
68 task.setDownloadsDirectory(
69 environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
70 task.setSystemSize(FileCollector.getSystemSize(this));
71 task.setLogOutputFile(new File(DUMPSYS_CACHE_PATH));
72 task.setAppCollector(collector);
73 task.setJobService(this, params);
74 AsyncTask.execute(task);
75 return true;
76 }
77
78 @Override
79 public boolean onStopJob(JobParameters params) {
80 // TODO: Try to stop being handled.
81 return false;
82 }
83
84 /**
85 * Schedules a DiskStats collection task. This task only runs on device idle while charging
86 * once every 24 hours.
87 * @param context Context to use to get a job scheduler.
88 */
89 public static void schedule(Context context) {
90 JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
91
92 js.schedule(new JobInfo.Builder(JOB_DISKSTATS_LOGGING, sDiskStatsLoggingService)
93 .setRequiresDeviceIdle(true)
94 .setRequiresCharging(true)
95 .setPeriodic(TimeUnit.DAYS.toMillis(1))
96 .build());
97 }
98
99 private static boolean isCharging(Context context) {
100 BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
101 if (batteryManager != null) {
102 return batteryManager.isCharging();
103 }
104 return false;
105 }
106
107 @VisibleForTesting
108 static class LogRunnable implements Runnable {
109 private static final long TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5);
110
111 private JobService mJobService;
112 private JobParameters mParams;
113 private AppCollector mCollector;
114 private File mOutputFile;
115 private File mRootDirectory;
116 private File mDownloadsDirectory;
117 private long mSystemSize;
118
119 public void setRootDirectory(File file) {
120 mRootDirectory = file;
121 }
122
123 public void setDownloadsDirectory(File file) {
124 mDownloadsDirectory = file;
125 }
126
127 public void setAppCollector(AppCollector collector) {
128 mCollector = collector;
129 }
130
131 public void setLogOutputFile(File file) {
132 mOutputFile = file;
133 }
134
135 public void setSystemSize(long size) {
136 mSystemSize = size;
137 }
138
139 public void setJobService(JobService jobService, JobParameters params) {
140 mJobService = jobService;
141 mParams = params;
142 }
143
144 public void run() {
145 FileCollector.MeasurementResult mainCategories =
146 FileCollector.getMeasurementResult(mRootDirectory);
147 FileCollector.MeasurementResult downloads =
148 FileCollector.getMeasurementResult(mDownloadsDirectory);
149
150 logToFile(mainCategories, downloads, mCollector.getPackageStats(TIMEOUT_MILLIS),
151 mSystemSize);
152
153 if (mJobService != null) {
154 mJobService.jobFinished(mParams, false);
155 }
156 }
157
158 private void logToFile(MeasurementResult mainCategories, MeasurementResult downloads,
159 List<PackageStats> stats, long systemSize) {
160 DiskStatsFileLogger logger = new DiskStatsFileLogger(mainCategories, downloads, stats,
161 systemSize);
162 try {
163 mOutputFile.createNewFile();
164 logger.dumpToFile(mOutputFile);
165 } catch (IOException e) {
166 Log.e(TAG, "Exception while writing opportunistic disk file cache.", e);
167 }
168 }
169 }
170}