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