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.managedprovisioning.common.Globals.ACTION_RESUME_PROVISIONING; 21 import static java.nio.charset.StandardCharsets.UTF_8; 22 23 import android.content.Context; 24 import android.content.Intent; 25 import android.nfc.NdefRecord; 26 import android.os.SystemClock; 27 import android.support.annotation.NonNull; 28 import android.support.annotation.Nullable; 29 30 import com.android.managedprovisioning.parser.PropertiesProvisioningDataParser; 31 import com.android.managedprovisioning.task.AbstractProvisioningTask; 32 33 import java.io.IOException; 34 import java.io.StringReader; 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Set; 38 import java.util.Properties; 39 40 /** 41 * Class containing various auxiliary methods used by provisioning analytics tracker. 42 */ 43 public class AnalyticsUtils { 44 45 public AnalyticsUtils() {} 46 47 private static final String PROVISIONING_EXTRA_PREFIX = "android.app.extra.PROVISIONING_"; 48 49 /** 50 * Returns package name of the installer package, null if package is not present on the device 51 * and empty string if installer package is not present on the device. 52 * 53 * @param context Context used to get package manager 54 * @param packageName Package name of the installed package 55 */ 56 @Nullable 57 public static String getInstallerPackageName(Context context, String packageName) { 58 try { 59 return context.getPackageManager().getInstallerPackageName(packageName); 60 } catch (IllegalArgumentException e) { 61 return null; 62 } 63 } 64 65 /** 66 * Returns elapsed real time. 67 */ 68 public Long elapsedRealTime() { 69 return SystemClock.elapsedRealtime(); 70 } 71 72 /** 73 * Returns list of all valid provisioning extras sent by the dpc. 74 * 75 * @param intent Intent that started provisioning 76 */ 77 @NonNull 78 public static List<String> getAllProvisioningExtras(Intent intent) { 79 if (intent == null || ACTION_RESUME_PROVISIONING.equals(intent.getAction())) { 80 // Provisioning extras should have already been logged for resume case. 81 return new ArrayList<String>(); 82 } else if (ACTION_NDEF_DISCOVERED.equals(intent.getAction())) { 83 return getExtrasFromProperties(intent); 84 } else { 85 return getExtrasFromBundle(intent); 86 } 87 } 88 89 /** 90 * Returns unique string for all provisioning task errors. 91 * 92 * @param task Provisioning task which threw error 93 * @param errorCode Unique code from class indicating the error 94 */ 95 @Nullable 96 public static String getErrorString(AbstractProvisioningTask task, int errorCode) { 97 if (task == null) { 98 return null; 99 } 100 // We do not have definite codes for all provisioning errors yet. We just pass the task's 101 // class name and the internal task's error code to generate a unique error code. 102 return task.getClass().getSimpleName() + ":" + errorCode; 103 } 104 105 @NonNull 106 private static List<String> getExtrasFromBundle(Intent intent) { 107 List<String> provisioningExtras = new ArrayList<String>(); 108 if (intent != null && intent.getExtras() != null) { 109 final Set<String> keys = intent.getExtras().keySet(); 110 for (String key : keys) { 111 if (isValidProvisioningExtra(key)) { 112 provisioningExtras.add(key); 113 } 114 } 115 } 116 return provisioningExtras; 117 } 118 119 @NonNull 120 private static List<String> getExtrasFromProperties(Intent intent) { 121 List<String> provisioningExtras = new ArrayList<String>(); 122 NdefRecord firstRecord = PropertiesProvisioningDataParser.getFirstNdefRecord(intent); 123 if (firstRecord != null) { 124 try { 125 Properties props = new Properties(); 126 props.load(new StringReader(new String(firstRecord.getPayload(), UTF_8))); 127 final Set<String> keys = props.stringPropertyNames(); 128 for (String key : keys) { 129 if (isValidProvisioningExtra(key)) { 130 provisioningExtras.add(key); 131 } 132 } 133 } catch (IOException e) { 134 } 135 } 136 return provisioningExtras; 137 } 138 139 /** 140 * Returns if a string is a valid provisioning extra. 141 */ 142 private static boolean isValidProvisioningExtra(String provisioningExtra) { 143 // Currently it verifies using the prefix. We should further change this to verify using the 144 // actual DPM extras. 145 return provisioningExtra != null && provisioningExtra.startsWith(PROVISIONING_EXTRA_PREFIX); 146 } 147 } 148