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.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
21 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER;
22 import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_QR_CODE;
23 import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_UNSPECIFIED;
24 import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED;
25 import static android.stats.devicepolicy.DevicePolicyEnums.PROVISIONING_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE;
26 
27 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ACTION;
28 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_CANCELLED;
29 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_COPY_ACCOUNT_STATUS;
30 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_DPC_INSTALLED_BY_PACKAGE;
31 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_DPC_PACKAGE_NAME;
32 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_NFC;
33 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_QR_CODE;
34 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE;
35 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ERROR;
36 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_EXTRA;
37 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_SESSION_COMPLETED;
38 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_SESSION_STARTED;
39 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_TERMS_COUNT;
40 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_TERMS_READ;
41 
42 import android.annotation.IntDef;
43 import android.app.admin.DevicePolicyEventLogger;
44 import android.app.admin.DevicePolicyManager;
45 import android.content.ComponentName;
46 import android.content.Context;
47 import android.content.Intent;
48 import android.stats.devicepolicy.DevicePolicyEnums;
49 
50 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences;
51 import com.android.managedprovisioning.common.Utils;
52 import com.android.managedprovisioning.model.ProvisioningParams;
53 import com.android.managedprovisioning.task.AbstractProvisioningTask;
54 import java.util.List;
55 
56 /**
57  * Utility class to log metrics.
58  */
59 public class ProvisioningAnalyticsTracker {
60 
61     private final MetricsLoggerWrapper mMetricsLoggerWrapper = new MetricsLoggerWrapper();
62 
63     // Only add to the end of the list. Do not change or rearrange these values, that will break
64     // historical data. Do not use negative numbers or zero, logger only handles positive
65     // integers.
66     public static final int CANCELLED_BEFORE_PROVISIONING = 1;
67     public static final int CANCELLED_DURING_PROVISIONING = 2;
68     public static final int CANCELLED_DURING_PROVISIONING_PREPARE = 3;
69     private final ManagedProvisioningSharedPreferences mSharedPreferences;
70 
71     @IntDef({
72         CANCELLED_BEFORE_PROVISIONING,
73         CANCELLED_DURING_PROVISIONING,
74         CANCELLED_DURING_PROVISIONING_PREPARE})
75     public @interface CancelState {}
76 
77     // Only add to the end of the list. Do not change or rearrange these values, that will break
78     // historical data. Do not use negative numbers or zero, logger only handles positive
79     // integers.
80     public static final int COPY_ACCOUNT_SUCCEEDED = 1;
81     public static final int COPY_ACCOUNT_FAILED = 2;
82     public static final int COPY_ACCOUNT_TIMED_OUT = 3;
83     public static final int COPY_ACCOUNT_EXCEPTION = 4;
84 
85     @IntDef({
86         COPY_ACCOUNT_SUCCEEDED,
87         COPY_ACCOUNT_FAILED,
88         COPY_ACCOUNT_TIMED_OUT,
89         COPY_ACCOUNT_EXCEPTION})
90     public @interface CopyAccountStatus {}
91 
92     private static final int PROVISIONING_FLOW_TYPE_ADMIN_INTEGRATED = 1;
93     private static final int PROVISIONING_FLOW_TYPE_LEGACY = 2;
94 
95     private final MetricsWriter mMetricsWriter;
96 
ProvisioningAnalyticsTracker(MetricsWriter metricsWriter, ManagedProvisioningSharedPreferences prefs)97     public ProvisioningAnalyticsTracker(MetricsWriter metricsWriter,
98             ManagedProvisioningSharedPreferences prefs) {
99         // Disables instantiation. Use getInstance() instead.
100         mMetricsWriter = metricsWriter;
101         mSharedPreferences = prefs;
102     }
103 
104     /**
105      * Logs some metrics when the provisioning starts.
106      *
107      * @param context Context passed to MetricsLogger
108      * @param params Provisioning params
109      */
logProvisioningStarted(Context context, ProvisioningParams params)110     public void logProvisioningStarted(Context context, ProvisioningParams params) {
111         logDpcPackageInformation(context, params.inferDeviceAdminPackageName());
112         logNetworkType(context);
113         maybeLogProvisioningFlowType(params);
114     }
115 
116     /**
117      * Logs some metrics when the preprovisioning starts.
118      *
119      * @param context Context passed to MetricsLogger
120      * @param intent Intent that started provisioning
121      */
logPreProvisioningStarted(Context context, Intent intent)122     public void logPreProvisioningStarted(Context context, Intent intent) {
123         logProvisioningExtras(context, intent);
124         maybeLogEntryPoint(context, intent);
125     }
126 
127     /**
128      * Logs status of copy account to user task.
129      *
130      * @param context Context passed to MetricsLogger
131      * @param status Status of copy account to user task
132      */
logCopyAccountStatus(Context context, @CopyAccountStatus int status)133     public void logCopyAccountStatus(Context context, @CopyAccountStatus int status) {
134         mMetricsLoggerWrapper.logAction(context, PROVISIONING_COPY_ACCOUNT_STATUS, status);
135         mMetricsWriter.write(DevicePolicyEventLogger
136                 .createEvent(DevicePolicyEnums.PROVISIONING_COPY_ACCOUNT_STATUS)
137                 .setInt(status)
138                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
139     }
140 
141     /**
142      * Logs when provisioning is cancelled.
143      *
144      * @param context Context passed to MetricsLogger
145      * @param cancelState State when provisioning was cancelled
146      */
logProvisioningCancelled(Context context, @CancelState int cancelState)147     public void logProvisioningCancelled(Context context, @CancelState int cancelState) {
148         mMetricsLoggerWrapper.logAction(context, PROVISIONING_CANCELLED, cancelState);
149         mMetricsWriter.write(DevicePolicyEventLogger
150                 .createEvent(DevicePolicyEnums.PROVISIONING_CANCELLED)
151                 .setInt(cancelState)
152                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
153     }
154 
155     /**
156      * Logs error during provisioning tasks.
157      *
158      * @param context Context passed to MetricsLogger
159      * @param task Provisioning task which threw error
160      * @param errorCode Code indicating the type of error that happened.
161      */
logProvisioningError(Context context, AbstractProvisioningTask task, int errorCode)162     public void logProvisioningError(Context context, AbstractProvisioningTask task,
163             int errorCode) {
164         mMetricsLoggerWrapper.logAction(context, PROVISIONING_ERROR,
165                 AnalyticsUtils.getErrorString(task, errorCode));
166         mMetricsWriter.write(DevicePolicyEventLogger
167                 .createEvent(DevicePolicyEnums.PROVISIONING_ERROR)
168                 .setStrings(AnalyticsUtils.getErrorString(task, errorCode))
169                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
170     }
171 
172     /**
173      * Logs error code, when provisioning is not allowed.
174      *
175      * @param context Context passed to MetricsLogger
176      * @param provisioningErrorCode Code indicating why provisioning is not allowed.
177      */
logProvisioningNotAllowed(Context context, int provisioningErrorCode)178     public void logProvisioningNotAllowed(Context context, int provisioningErrorCode) {
179         mMetricsLoggerWrapper.logAction(context, PROVISIONING_ERROR, provisioningErrorCode);
180         mMetricsWriter.write(DevicePolicyEventLogger
181                 .createEvent(DevicePolicyEnums.PROVISIONING_ERROR)
182                 .setStrings(String.valueOf(provisioningErrorCode))
183                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
184     }
185 
186     /**
187      * logs when a provisioning session has started.
188      *
189      * @param context Context passed to MetricsLogger
190      */
logProvisioningSessionStarted(Context context)191     public void logProvisioningSessionStarted(Context context) {
192         mMetricsLoggerWrapper.logAction(context, PROVISIONING_SESSION_STARTED);
193         mMetricsWriter.write(DevicePolicyEventLogger
194                 .createEvent(DevicePolicyEnums.PROVISIONING_SESSION_STARTED)
195                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
196     }
197 
198     /**
199      * logs when a provisioning session has completed.
200      *
201      * @param context Context passed to MetricsLogger
202      */
logProvisioningSessionCompleted(Context context)203     public void logProvisioningSessionCompleted(Context context) {
204         mMetricsLoggerWrapper.logAction(context, PROVISIONING_SESSION_COMPLETED);
205         mMetricsWriter.write(DevicePolicyEventLogger
206                 .createEvent(DevicePolicyEnums.PROVISIONING_SESSION_COMPLETED)
207                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
208     }
209 
210     /**
211      * logs number of terms displayed on the terms screen.
212      *
213      * @param context Context passed to MetricsLogger
214      * @param count Number of terms displayed
215      */
logNumberOfTermsDisplayed(Context context, int count)216     public void logNumberOfTermsDisplayed(Context context, int count) {
217         mMetricsLoggerWrapper.logAction(context, PROVISIONING_TERMS_COUNT, count);
218         mMetricsWriter.write(DevicePolicyEventLogger
219                 .createEvent(DevicePolicyEnums.PROVISIONING_TERMS_COUNT)
220                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
221     }
222 
223     /**
224      * logs number of terms read on the terms screen.
225      *
226      * @param context Context passed to MetricsLogger
227      * @param count Number of terms read
228      */
logNumberOfTermsRead(Context context, int count)229     public void logNumberOfTermsRead(Context context, int count) {
230         mMetricsLoggerWrapper.logAction(context, PROVISIONING_TERMS_READ, count);
231         mMetricsWriter.write(DevicePolicyEventLogger
232                 .createEvent(DevicePolicyEnums.PROVISIONING_TERMS_READ)
233                 .setInt(count)
234                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
235     }
236 
237     /**
238      * Logs when the provisioning preparation has started.
239      * <p>The preparation includes network setup, downloading, verifying and installing the
240      * admin app.
241      */
logProvisioningPrepareStarted()242     public void logProvisioningPrepareStarted() {
243         mMetricsWriter.write(DevicePolicyEventLogger
244                 .createEvent(DevicePolicyEnums.PROVISIONING_PREPARE_STARTED)
245                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
246     }
247 
248     /**
249      * Logs when the provisioning preparation has completed.
250      * <p>The preparation includes network setup, downloading, verifying and installing the
251      * admin app.
252      */
logProvisioningPrepareCompleted()253     public void logProvisioningPrepareCompleted() {
254         mMetricsWriter.write(DevicePolicyEventLogger
255                 .createEvent(DevicePolicyEnums.PROVISIONING_PREPARE_COMPLETED)
256                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
257     }
258 
logTimeLoggerEvent(int devicePolicyEvent, int time)259     public void logTimeLoggerEvent(int devicePolicyEvent, int time) {
260         mMetricsWriter.write(DevicePolicyEventLogger
261                 .createEvent(devicePolicyEvent)
262                 .setInt(time)
263                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
264     }
265 
266     /**
267      * Logs the provisioning action.
268      *  @param context Context passed to MetricsLogger
269      * @param provisioningAction Action that triggered provisioning
270      */
logProvisioningAction(Context context, String provisioningAction)271     public void logProvisioningAction(Context context, String provisioningAction) {
272         mMetricsLoggerWrapper.logAction(context, PROVISIONING_ACTION, provisioningAction);
273         mMetricsWriter.write(DevicePolicyEventLogger
274                 .createEvent(DevicePolicyEnums.PROVISIONING_ACTION)
275                 .setStrings(provisioningAction)
276                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
277         maybeLogManagedProfileOnFullyManagedDevice(context, provisioningAction);
278     }
279 
280     /**
281      * Logs the type of provisioning flow if this is organization owned provisioning.
282      * <p>It would be either admin integrated flow or legacy.
283      *
284      * @param params Used to extract whether this is the admin integrated flow
285      */
maybeLogProvisioningFlowType(ProvisioningParams params)286     private void maybeLogProvisioningFlowType(ProvisioningParams params) {
287         if (!params.isOrganizationOwnedProvisioning) {
288             return;
289         }
290         final boolean isAdminIntegratedFlow = new Utils().isAdminIntegratedFlow(params);
291         mMetricsWriter.write(DevicePolicyEventLogger
292                 .createEvent(DevicePolicyEnums.PROVISIONING_FLOW_TYPE)
293                 .setInt(isAdminIntegratedFlow
294                         ? PROVISIONING_FLOW_TYPE_ADMIN_INTEGRATED
295                         : PROVISIONING_FLOW_TYPE_LEGACY)
296                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
297     }
298 
299     /**
300      * Logs all the provisioning extras passed by the dpc.
301      *
302      * @param context Context passed to MetricsLogger
303      * @param intent Intent that started provisioning
304      */
logProvisioningExtras(Context context, Intent intent)305     private void logProvisioningExtras(Context context, Intent intent) {
306         final List<String> provisioningExtras = AnalyticsUtils.getAllProvisioningExtras(intent);
307         for (String extra : provisioningExtras) {
308             mMetricsLoggerWrapper.logAction(context, PROVISIONING_EXTRA, extra);
309         }
310         mMetricsWriter.write(DevicePolicyEventLogger
311                 .createEvent(DevicePolicyEnums.PROVISIONING_EXTRAS)
312                 .setStrings(provisioningExtras.toArray(new String[0]))
313                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
314     }
315 
316     /**
317      * Logs some entry points to provisioning.
318      *
319      * @param context Context passed to MetricsLogger
320      * @param intent Intent that started provisioning
321      */
maybeLogEntryPoint(Context context, Intent intent)322     private void maybeLogEntryPoint(Context context, Intent intent) {
323         if (intent == null || intent.getAction() == null) {
324             return;
325         }
326         switch (intent.getAction()) {
327             case ACTION_NDEF_DISCOVERED:
328                 mMetricsLoggerWrapper.logAction(context, PROVISIONING_ENTRY_POINT_NFC);
329                 mMetricsWriter.write(DevicePolicyEventLogger
330                         .createEvent(DevicePolicyEnums.PROVISIONING_ENTRY_POINT_NFC)
331                         .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
332                 break;
333             case ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE:
334                 logProvisionedFromTrustedSource(context, intent);
335                 break;
336         }
337     }
338 
logProvisionedFromTrustedSource(Context context, Intent intent)339     private void logProvisionedFromTrustedSource(Context context, Intent intent) {
340         mMetricsLoggerWrapper.logAction(context, PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE);
341         final int provisioningTrigger = intent.getIntExtra(EXTRA_PROVISIONING_TRIGGER,
342                 PROVISIONING_TRIGGER_UNSPECIFIED);
343         if (provisioningTrigger == PROVISIONING_TRIGGER_QR_CODE) {
344             mMetricsLoggerWrapper.logAction(context, PROVISIONING_ENTRY_POINT_QR_CODE);
345         }
346         mMetricsWriter.write(DevicePolicyEventLogger
347                 .createEvent(DevicePolicyEnums.PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE)
348                 .setInt(provisioningTrigger)
349                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
350     }
351 
352     /**
353      * Logs package information of the dpc.
354      *
355      * @param context Context passed to MetricsLogger
356      * @param dpcPackageName Package name of the dpc
357      */
logDpcPackageInformation(Context context, String dpcPackageName)358     private void logDpcPackageInformation(Context context, String dpcPackageName) {
359         // Logs package name of the dpc.
360         mMetricsLoggerWrapper.logAction(context, PROVISIONING_DPC_PACKAGE_NAME, dpcPackageName);
361         mMetricsWriter.write(DevicePolicyEventLogger
362                 .createEvent(DevicePolicyEnums.PROVISIONING_DPC_PACKAGE_NAME)
363                 .setStrings(dpcPackageName)
364                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
365 
366         // Logs package name of the package which installed dpc.
367         final String dpcInstallerPackage =
368                 AnalyticsUtils.getInstallerPackageName(context, dpcPackageName);
369         mMetricsLoggerWrapper.logAction(context, PROVISIONING_DPC_INSTALLED_BY_PACKAGE,
370                 dpcInstallerPackage);
371         mMetricsWriter.write(DevicePolicyEventLogger
372                 .createEvent(DevicePolicyEnums.PROVISIONING_DPC_INSTALLED_BY_PACKAGE)
373                 .setStrings(dpcInstallerPackage)
374                 .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
375     }
376 
377     /**
378      * Logs the network type to which the device is connected.
379      *
380      * @param context Context passed to MetricsLogger
381      */
logNetworkType(Context context)382     private void logNetworkType(Context context) {
383         NetworkTypeLogger networkTypeLogger = new NetworkTypeLogger(context);
384         networkTypeLogger.log();
385     }
386 
maybeLogManagedProfileOnFullyManagedDevice(Context context, String provisioningAction)387     private void maybeLogManagedProfileOnFullyManagedDevice(Context context,
388             String provisioningAction) {
389         final DevicePolicyManager dpm =
390                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
391         final ComponentName currentDeviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
392         if (currentDeviceOwner != null
393                 && ACTION_PROVISION_MANAGED_PROFILE.equals(provisioningAction)) {
394             mMetricsWriter.write(DevicePolicyEventLogger
395                     .createEvent(PROVISIONING_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE)
396                     .setTimePeriod(AnalyticsUtils.getProvisioningTime(mSharedPreferences)));
397         }
398     }
399 }
400