1 /*
2  * Copyright (C) 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.task;
18 
19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_START_PROFILE_TASK_MS;
20 import static com.android.internal.util.Preconditions.checkNotNull;
21 
22 import android.app.ActivityManager;
23 import android.app.IActivityManager;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.os.RemoteException;
29 import android.os.UserHandle;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.managedprovisioning.analytics.MetricsWriterFactory;
33 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
34 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences;
35 import com.android.managedprovisioning.common.ProvisionLogger;
36 import com.android.managedprovisioning.R;
37 import com.android.managedprovisioning.common.SettingsFacade;
38 import com.android.managedprovisioning.model.ProvisioningParams;
39 
40 import java.util.concurrent.Semaphore;
41 import java.util.concurrent.TimeUnit;
42 
43 /**
44  * This task starts the managed profile and waits for it to be unlocked.
45  */
46 public class StartManagedProfileTask extends AbstractProvisioningTask {
47     // Maximum time we will wait for ACTION_USER_UNLOCK until we give up
48     private static final int USER_UNLOCKED_TIMEOUT_SECONDS = 120; // 2 minutes
49     @VisibleForTesting
50     static final IntentFilter UNLOCK_FILTER = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
51 
52     private final IActivityManager mIActivityManager;
53 
StartManagedProfileTask(Context context, ProvisioningParams params, Callback callback)54     public StartManagedProfileTask(Context context, ProvisioningParams params, Callback callback) {
55         this(ActivityManager.getService(), context, params, callback,
56                 new ProvisioningAnalyticsTracker(
57                         MetricsWriterFactory.getMetricsWriter(context, new SettingsFacade()),
58                         new ManagedProvisioningSharedPreferences(context)));
59     }
60 
61     @VisibleForTesting
StartManagedProfileTask( IActivityManager iActivityManager, Context context, ProvisioningParams params, Callback callback, ProvisioningAnalyticsTracker provisioningAnalyticsTracker)62     StartManagedProfileTask(
63             IActivityManager iActivityManager,
64             Context context,
65             ProvisioningParams params,
66             Callback callback,
67             ProvisioningAnalyticsTracker provisioningAnalyticsTracker) {
68         super(context, params, callback, provisioningAnalyticsTracker);
69 
70         mIActivityManager = checkNotNull(iActivityManager);
71     }
72 
73     @Override
run(int userId)74     public void run(int userId) {
75         startTaskTimer();
76         UserUnlockedReceiver unlockedReceiver = new UserUnlockedReceiver(userId);
77         mContext.registerReceiverAsUser(unlockedReceiver, new UserHandle(userId), UNLOCK_FILTER,
78                 null, null);
79         try {
80             if (!mIActivityManager.startUserInBackground(userId)) {
81                 ProvisionLogger.loge("Unable to start user in background: " + userId);
82                 error(0);
83                 return;
84             }
85 
86             if (!unlockedReceiver.waitForUserUnlocked()) {
87                 ProvisionLogger.loge("Timeout whilst waiting for unlock of user: " + userId);
88                 error(0);
89                 return;
90             }
91         } catch (RemoteException e) {
92             ProvisionLogger.loge("Exception when starting user in background: " + userId, e);
93             error(0);
94             return;
95         } finally {
96             mContext.unregisterReceiver(unlockedReceiver);
97         }
98         stopTaskTimer();
99         success();
100     }
101 
102     @Override
getStatusMsgId()103     public int getStatusMsgId() {
104         return R.string.progress_finishing_touches;
105     }
106 
107     @Override
getMetricsCategory()108     protected int getMetricsCategory() {
109         return PROVISIONING_START_PROFILE_TASK_MS;
110     }
111 
112     /**
113      * BroadcastReceiver that listens to {@link Intent#ACTION_USER_UNLOCKED} in order to provide
114      * a blocking wait until the managed profile has been started and unlocked.
115      */
116     @VisibleForTesting
117     static class UserUnlockedReceiver extends BroadcastReceiver {
118         private final Semaphore semaphore = new Semaphore(0);
119         private final int mUserId;
120 
UserUnlockedReceiver(int userId)121         UserUnlockedReceiver(int userId) {
122             mUserId = userId;
123         }
124 
125         @Override
onReceive(Context context, Intent intent )126         public void onReceive(Context context, Intent intent ) {
127             if (!Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
128                 ProvisionLogger.logw("Unexpected intent: " + intent);
129                 return;
130             }
131             if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == mUserId) {
132                 ProvisionLogger.logd("Received ACTION_USER_UNLOCKED for user " + mUserId);
133                 semaphore.release();
134             }
135         }
136 
waitForUserUnlocked()137         public boolean waitForUserUnlocked() {
138             ProvisionLogger.logd("Waiting for ACTION_USER_UNLOCKED");
139             try {
140                 return semaphore.tryAcquire(USER_UNLOCKED_TIMEOUT_SECONDS, TimeUnit.SECONDS);
141             } catch (InterruptedException ie) {
142                 return false;
143             }
144         }
145     }
146 }
147