1 /* 2 * Copyright 2016, 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 android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED; 20 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_COPY_ACCOUNT_TASK_MS; 21 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_CREATE_PROFILE_TASK_MS; 22 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS; 23 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS; 24 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_INSTALL_PACKAGE_TASK_MS; 25 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS; 26 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS; 27 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_START_PROFILE_TASK_MS; 28 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_TERMS_ACTIVITY_TIME_MS; 29 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_TOTAL_TASK_TIME_MS; 30 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_WEB_ACTIVITY_TIME_MS; 31 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.VIEW_UNKNOWN; 32 import static com.android.managedprovisioning.common.Globals.ACTION_RESUME_PROVISIONING; 33 import static java.nio.charset.StandardCharsets.UTF_8; 34 35 import android.content.Context; 36 import android.content.Intent; 37 import android.icu.util.TimeUnit; 38 import android.nfc.NdefRecord; 39 import android.os.SystemClock; 40 import android.stats.devicepolicy.DevicePolicyEnums; 41 import androidx.annotation.NonNull; 42 import androidx.annotation.Nullable; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.managedprovisioning.analytics.TimeLogger.TimeCategory; 46 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences; 47 import com.android.managedprovisioning.parser.PropertiesProvisioningDataParser; 48 import com.android.managedprovisioning.task.AbstractProvisioningTask; 49 50 import java.io.IOException; 51 import java.io.StringReader; 52 import java.time.Clock; 53 import java.util.ArrayList; 54 import java.util.List; 55 import java.util.Properties; 56 import java.util.Set; 57 import java.util.function.Function; 58 import java.util.function.LongSupplier; 59 60 /** 61 * Class containing various auxiliary methods used by provisioning analytics tracker. 62 */ 63 public class AnalyticsUtils { 64 65 final static int CATEGORY_VIEW_UNKNOWN = -1; 66 AnalyticsUtils()67 public AnalyticsUtils() {} 68 69 private static final String PROVISIONING_EXTRA_PREFIX = "android.app.extra.PROVISIONING_"; 70 71 /** 72 * Returns package name of the installer package, null if package is not present on the device 73 * and empty string if installer package is not present on the device. 74 * 75 * @param context Context used to get package manager 76 * @param packageName Package name of the installed package 77 */ 78 @Nullable getInstallerPackageName(Context context, String packageName)79 public static String getInstallerPackageName(Context context, String packageName) { 80 try { 81 return context.getPackageManager().getInstallerPackageName(packageName); 82 } catch (IllegalArgumentException e) { 83 return null; 84 } 85 } 86 87 /** 88 * Returns elapsed real time. 89 */ elapsedRealTime()90 public Long elapsedRealTime() { 91 return SystemClock.elapsedRealtime(); 92 } 93 94 /** 95 * Returns list of all valid provisioning extras sent by the dpc. 96 * 97 * @param intent Intent that started provisioning 98 */ 99 @NonNull getAllProvisioningExtras(Intent intent)100 public static List<String> getAllProvisioningExtras(Intent intent) { 101 if (intent == null || ACTION_RESUME_PROVISIONING.equals(intent.getAction())) { 102 // Provisioning extras should have already been logged for resume case. 103 return new ArrayList<String>(); 104 } else if (ACTION_NDEF_DISCOVERED.equals(intent.getAction())) { 105 return getExtrasFromProperties(intent); 106 } else { 107 return getExtrasFromBundle(intent); 108 } 109 } 110 111 /** 112 * Returns unique string for all provisioning task errors. 113 * 114 * @param task Provisioning task which threw error 115 * @param errorCode Unique code from class indicating the error 116 */ 117 @Nullable getErrorString(AbstractProvisioningTask task, int errorCode)118 public static String getErrorString(AbstractProvisioningTask task, int errorCode) { 119 if (task == null) { 120 return null; 121 } 122 // We do not have definite codes for all provisioning errors yet. We just pass the task's 123 // class name and the internal task's error code to generate a unique error code. 124 return task.getClass().getSimpleName() + ":" + errorCode; 125 } 126 127 @NonNull getExtrasFromBundle(Intent intent)128 private static List<String> getExtrasFromBundle(Intent intent) { 129 List<String> provisioningExtras = new ArrayList<String>(); 130 if (intent != null && intent.getExtras() != null) { 131 final Set<String> keys = intent.getExtras().keySet(); 132 for (String key : keys) { 133 if (isValidProvisioningExtra(key)) { 134 provisioningExtras.add(key); 135 } 136 } 137 } 138 return provisioningExtras; 139 } 140 141 @NonNull getExtrasFromProperties(Intent intent)142 private static List<String> getExtrasFromProperties(Intent intent) { 143 List<String> provisioningExtras = new ArrayList<String>(); 144 NdefRecord firstRecord = PropertiesProvisioningDataParser.getFirstNdefRecord(intent); 145 if (firstRecord != null) { 146 try { 147 Properties props = new Properties(); 148 props.load(new StringReader(new String(firstRecord.getPayload(), UTF_8))); 149 final Set<String> keys = props.stringPropertyNames(); 150 for (String key : keys) { 151 if (isValidProvisioningExtra(key)) { 152 provisioningExtras.add(key); 153 } 154 } 155 } catch (IOException e) { 156 } 157 } 158 return provisioningExtras; 159 } 160 161 /** 162 * Returns if a string is a valid provisioning extra. 163 */ isValidProvisioningExtra(String provisioningExtra)164 private static boolean isValidProvisioningExtra(String provisioningExtra) { 165 // Currently it verifies using the prefix. We should further change this to verify using the 166 // actual DPM extras. 167 return provisioningExtra != null && provisioningExtra.startsWith(PROVISIONING_EXTRA_PREFIX); 168 } 169 170 /** 171 * Converts from {@link MetricsEvent} constants to {@link DevicePolicyEnums} constants. 172 * <p>If such a {@link MetricsEvent} does not exist, the metric is assumed 173 * to belong to {@link DevicePolicyEnums}. 174 */ getDevicePolicyEventForCategory(@imeCategory int metricsEvent)175 static int getDevicePolicyEventForCategory(@TimeCategory int metricsEvent) { 176 switch (metricsEvent) { 177 case PROVISIONING_COPY_ACCOUNT_TASK_MS: 178 return DevicePolicyEnums.PROVISIONING_COPY_ACCOUNT_TASK_MS; 179 case PROVISIONING_CREATE_PROFILE_TASK_MS: 180 return DevicePolicyEnums.PROVISIONING_CREATE_PROFILE_TASK_MS; 181 case PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS: 182 return DevicePolicyEnums.PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS; 183 case PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS: 184 return DevicePolicyEnums.PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS; 185 case PROVISIONING_INSTALL_PACKAGE_TASK_MS: 186 return DevicePolicyEnums.PROVISIONING_INSTALL_PACKAGE_TASK_MS; 187 case PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS: 188 return DevicePolicyEnums.PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS; 189 case PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS: 190 return DevicePolicyEnums.PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS; 191 case PROVISIONING_START_PROFILE_TASK_MS: 192 return DevicePolicyEnums.PROVISIONING_START_PROFILE_TASK_MS; 193 case PROVISIONING_WEB_ACTIVITY_TIME_MS: 194 return DevicePolicyEnums.PROVISIONING_WEB_ACTIVITY_TIME_MS; 195 case PROVISIONING_TERMS_ACTIVITY_TIME_MS: 196 return DevicePolicyEnums.PROVISIONING_TERMS_ACTIVITY_TIME_MS; 197 case PROVISIONING_TOTAL_TASK_TIME_MS: 198 return DevicePolicyEnums.PROVISIONING_TOTAL_TASK_TIME_MS; 199 case VIEW_UNKNOWN: 200 return -1; 201 default: 202 return metricsEvent; 203 } 204 } 205 206 /** 207 * Returns the time passed since provisioning started, in milliseconds. 208 * Returns <code>-1</code> if the provisioning start time was not specified via 209 * {@link ManagedProvisioningSharedPreferences#writeProvisioningStartedTimestamp(long)}. 210 */ getProvisioningTime(ManagedProvisioningSharedPreferences sharedPreferences)211 static long getProvisioningTime(ManagedProvisioningSharedPreferences sharedPreferences) { 212 return getProvisioningTime(sharedPreferences, SystemClock::elapsedRealtime); 213 } 214 215 @VisibleForTesting getProvisioningTime(ManagedProvisioningSharedPreferences sharedPreferences, LongSupplier getTimeFunction)216 static long getProvisioningTime(ManagedProvisioningSharedPreferences sharedPreferences, 217 LongSupplier getTimeFunction) { 218 if (sharedPreferences.getProvisioningStartedTimestamp() == 0) { 219 return -1; 220 } 221 return getTimeFunction.getAsLong() - sharedPreferences.getProvisioningStartedTimestamp(); 222 } 223 } 224