1 /*
2  * Copyright (C) 2022 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.ondevicepersonalization.services;
18 
19 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
20 
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.odp.module.common.DeviceUtils;
29 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
30 import com.android.ondevicepersonalization.services.data.user.UserDataCollectionJobService;
31 import com.android.ondevicepersonalization.services.download.mdd.MobileDataDownloadFactory;
32 import com.android.ondevicepersonalization.services.maintenance.OnDevicePersonalizationMaintenanceJob;
33 
34 import com.google.common.util.concurrent.FutureCallback;
35 import com.google.common.util.concurrent.Futures;
36 import com.google.common.util.concurrent.ListenableFuture;
37 
38 import java.util.concurrent.Executor;
39 
40 /** BroadcastReceiver used to schedule OnDevicePersonalization jobs/workers. */
41 public class OnDevicePersonalizationBroadcastReceiver extends BroadcastReceiver {
42     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
43     private static final String TAG = "OnDevicePersonalizationBroadcastReceiver";
44     private final Executor mExecutor;
45 
OnDevicePersonalizationBroadcastReceiver()46     public OnDevicePersonalizationBroadcastReceiver() {
47         this.mExecutor = OnDevicePersonalizationExecutors.getLightweightExecutor();
48     }
49 
50     @VisibleForTesting
OnDevicePersonalizationBroadcastReceiver(Executor executor)51     public OnDevicePersonalizationBroadcastReceiver(Executor executor) {
52         this.mExecutor = executor;
53     }
54 
55     /** Enable the OnDevicePersonalizationBroadcastReceiver */
enableReceiver(Context context)56     public static boolean enableReceiver(Context context) {
57         try {
58             context.getPackageManager()
59                     .setComponentEnabledSetting(
60                             new ComponentName(
61                                     context, OnDevicePersonalizationBroadcastReceiver.class),
62                             COMPONENT_ENABLED_STATE_ENABLED,
63                             PackageManager.DONT_KILL_APP);
64         } catch (IllegalArgumentException e) {
65             sLogger.e(TAG + ": enableService failed for " + context.getPackageName(), e);
66             return false;
67         }
68         return true;
69     }
70 
71     /** Called when the broadcast is received. OnDevicePersonalization jobs will be started here. */
onReceive(Context context, Intent intent)72     public void onReceive(Context context, Intent intent) {
73         if (FlagsFactory.getFlags().getGlobalKillSwitch()) {
74             sLogger.d(TAG + ": GlobalKillSwitch on, skipped broadcast.");
75             return;
76         }
77 
78         if (!DeviceUtils.isOdpSupported(context)) {
79             sLogger.d(TAG + ": Unsupported device, skipped broadcast.");
80             return;
81         }
82 
83         sLogger.d(TAG + ": onReceive() with intent + " + intent.getAction());
84 
85         if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
86             sLogger.d(TAG + ": Received unexpected intent " + intent.getAction());
87             return;
88         }
89         final PendingResult pendingResult = goAsync();
90         // Schedule MDD to download scripts periodically.
91         Futures.addCallback(
92                 restoreOdpJobs(context, mExecutor),
93                 new FutureCallback<Void>() {
94                     @Override
95                     public void onSuccess(Void result) {
96                         sLogger.d(TAG + ": Successfully scheduled MDD tasks.");
97                         pendingResult.finish();
98                     }
99                     @Override
100                     public void onFailure(Throwable t) {
101                         sLogger.e(TAG + ": Failed to schedule MDD tasks.", t);
102                         pendingResult.finish();
103                     }
104                 },
105                 mExecutor);
106     }
107 
108     /**
109      * Restores periodic jobs scheduling.
110      */
restoreOdpJobs(Context context, Executor executor)111     public static ListenableFuture<Void> restoreOdpJobs(Context context, Executor executor) {
112         if (FlagsFactory.getFlags().getGlobalKillSwitch() || !DeviceUtils.isOdpSupported(context)) {
113             sLogger.d(TAG + ": ODP disabled or unsupported device");
114             return null;
115         }
116 
117         var unused =
118                 Futures.submit(
119                         () -> {
120                             // Schedule maintenance task
121                             OnDevicePersonalizationMaintenanceJob.schedule(context);
122                             // Schedule user data collection task
123                             UserDataCollectionJobService.schedule(context);
124                         },
125                         executor);
126 
127         // Schedule MDD to download scripts periodically.
128         return MobileDataDownloadFactory.getMdd(context).schedulePeriodicBackgroundTasks();
129     }
130 }
131