1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.managedprovisioning.analytics; 18 19 import static com.android.internal.util.Preconditions.checkNotNull; 20 21 import android.app.admin.DevicePolicyEventLogger; 22 import android.app.job.JobParameters; 23 import android.app.job.JobService; 24 import android.os.AsyncTask; 25 import android.os.PersistableBundle; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.managedprovisioning.DevicePolicyProtos.DevicePolicyEvent; 29 import com.android.managedprovisioning.common.ProvisionLogger; 30 31 32 import java.io.File; 33 import java.io.FileInputStream; 34 import java.io.IOException; 35 import java.io.InputStream; 36 37 /** 38 * A {@link JobService} that reads the logs from the {@link InputStream} written to by 39 * {@link DeferredMetricsWriter} and writes them using another {@link MetricsWriter}. 40 * 41 * @see DeferredMetricsWriter 42 */ 43 public class ProcessMetricsJobService extends JobService { 44 45 static String EXTRA_FILE_PATH = "extra_file_path"; 46 47 private final MetricsWriter mMetricsWriter; 48 49 @VisibleForTesting ProcessMetricsJobService(MetricsWriter metricsWriter)50 ProcessMetricsJobService(MetricsWriter metricsWriter) { 51 mMetricsWriter = metricsWriter; 52 } 53 ProcessMetricsJobService()54 public ProcessMetricsJobService() { 55 this(new InstantMetricsWriter()); 56 } 57 58 @Override onStartJob(JobParameters params)59 public boolean onStartJob(JobParameters params) { 60 final PersistableBundle extras = params.getExtras(); 61 if (extras == null || !extras.containsKey(EXTRA_FILE_PATH)) { 62 return false; 63 } 64 final File metrics = new File(extras.getString(EXTRA_FILE_PATH)); 65 if (!metrics.exists()) { 66 return false; 67 } 68 executeReadDeferredMetrics(params, metrics); 69 return true; 70 } 71 72 @VisibleForTesting executeReadDeferredMetrics(JobParameters params, File metricsFile)73 void executeReadDeferredMetrics(JobParameters params, 74 File metricsFile) { 75 new ReadDeferredMetricsAsyncTask(params, metricsFile, mMetricsWriter).execute(); 76 } 77 78 @Override onStopJob(JobParameters params)79 public boolean onStopJob(JobParameters params) { 80 return false; 81 } 82 83 /** 84 * An {@link AsyncTask} which reads the logs from the {@link File} specified in the constructor 85 * and writes them to the specified {@link MetricsWriter}. 86 * 87 * <p>The {@link File} will be deleted after they are written to the {@link MetricsWriter}. 88 */ 89 private class ReadDeferredMetricsAsyncTask extends AsyncTask<Void, Void, Void> { 90 private static final int METRICS_INTERVAL_MILLIS = 10; 91 private final MetricsWriter mMetricsWriter; 92 private final File mFile; 93 private final JobParameters mJobParameters; 94 ReadDeferredMetricsAsyncTask(JobParameters params, File file, MetricsWriter metricsWriter)95 ReadDeferredMetricsAsyncTask(JobParameters params, 96 File file, 97 MetricsWriter metricsWriter) { 98 mFile = checkNotNull(file); 99 mMetricsWriter = metricsWriter; 100 mJobParameters = params; 101 } 102 103 @Override doInBackground(Void... voids)104 protected Void doInBackground(Void... voids) { 105 try (InputStream inputStream = new FileInputStream(mFile)) { 106 DevicePolicyEvent event; 107 while ((event = DevicePolicyEvent.parseDelimitedFrom(inputStream)) != null) { 108 delayProcessMetric(); 109 mMetricsWriter.write(devicePolicyEventToLogger(event)); 110 } 111 } catch (IOException e) { 112 ProvisionLogger.loge( 113 "Could not parse DevicePolicyEvent while reading from stream.", e); 114 } finally { 115 mFile.delete(); 116 } 117 return null; 118 } 119 120 @Override onPostExecute(Void aVoid)121 protected void onPostExecute(Void aVoid) { 122 jobFinished(mJobParameters, false); 123 } 124 125 /** 126 * Waits for {@link #METRICS_INTERVAL_MILLIS}. 127 * <p>statsd cannot handle too many metrics at once, so we must wait between each 128 * {@link MetricsWriter#write(DevicePolicyEventLogger...)} call. 129 */ delayProcessMetric()130 private void delayProcessMetric() { 131 try { 132 Thread.sleep(METRICS_INTERVAL_MILLIS); 133 } catch (InterruptedException e) { 134 ProvisionLogger.loge( 135 "Thread interrupted while waiting to log metric.", e); 136 } 137 } 138 devicePolicyEventToLogger(DevicePolicyEvent event)139 private DevicePolicyEventLogger devicePolicyEventToLogger(DevicePolicyEvent event) { 140 final DevicePolicyEventLogger eventLogger = DevicePolicyEventLogger 141 .createEvent(event.getEventId()) 142 .setAdmin(event.getAdminPackageName()) 143 .setInt(event.getIntegerValue()) 144 .setBoolean(event.getBooleanValue()) 145 .setTimePeriod(event.getTimePeriodMillis()); 146 if (event.getStringListValueCount() > 0) { 147 eventLogger.setStrings(event.getStringListValueList().toArray(new String[0])); 148 } 149 return eventLogger; 150 } 151 } 152 } 153