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_COPY_ACCOUNT_TASK_MS;
20 import static com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker.COPY_ACCOUNT_EXCEPTION;
21 import static com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker.COPY_ACCOUNT_FAILED;
22 import static com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker.COPY_ACCOUNT_SUCCEEDED;
23 import static com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker.COPY_ACCOUNT_TIMED_OUT;
24 
25 import android.accounts.Account;
26 import android.accounts.AccountManager;
27 import android.accounts.AuthenticatorException;
28 import android.accounts.OperationCanceledException;
29 import android.content.Context;
30 import android.os.UserHandle;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.managedprovisioning.analytics.MetricsWriter;
34 import com.android.managedprovisioning.analytics.MetricsWriterFactory;
35 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
36 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences;
37 import com.android.managedprovisioning.common.ProvisionLogger;
38 import com.android.managedprovisioning.R;
39 import com.android.managedprovisioning.common.SettingsFacade;
40 import com.android.managedprovisioning.model.ProvisioningParams;
41 
42 import java.io.IOException;
43 import java.util.concurrent.TimeUnit;
44 
45 /**
46  * This task copies the account in {@link ProvisioningParams#accountToMigrate} from an existing
47  * user to the user that is being provisioned.
48  *
49  * <p>If the account migration fails or times out, we still return success as we consider account
50  * migration not to be a critical operation.</p>
51  */
52 public class CopyAccountToUserTask extends AbstractProvisioningTask {
53     private static final int ACCOUNT_COPY_TIMEOUT_SECONDS = 60 * 3;  // 3 minutes
54 
55     private final int mSourceUserId;
56     private final ProvisioningAnalyticsTracker mProvisioningAnalyticsTracker;
57 
CopyAccountToUserTask( int sourceUserId, Context context, ProvisioningParams provisioningParams, Callback callback)58     public CopyAccountToUserTask(
59             int sourceUserId,
60             Context context,
61             ProvisioningParams provisioningParams,
62             Callback callback) {
63         this(sourceUserId, context, provisioningParams, callback,
64                 new ProvisioningAnalyticsTracker(
65                         MetricsWriterFactory.getMetricsWriter(context, new SettingsFacade()),
66                         new ManagedProvisioningSharedPreferences(context)));
67     }
68 
69     @VisibleForTesting
CopyAccountToUserTask( int sourceUserId, Context context, ProvisioningParams provisioningParams, Callback callback, ProvisioningAnalyticsTracker provisioningAnalyticsTracker)70     CopyAccountToUserTask(
71             int sourceUserId,
72             Context context,
73             ProvisioningParams provisioningParams,
74             Callback callback,
75             ProvisioningAnalyticsTracker provisioningAnalyticsTracker) {
76         super(context, provisioningParams, callback, provisioningAnalyticsTracker);
77         mSourceUserId = sourceUserId;
78         mProvisioningAnalyticsTracker = provisioningAnalyticsTracker;
79     }
80 
81     @Override
run(int userId)82     public void run(int userId) {
83         startTaskTimer();
84 
85         final boolean copySucceeded = maybeCopyAccount(userId);
86         // Do not log time if account migration did not succeed, as that isn't useful.
87         if (copySucceeded) {
88             stopTaskTimer();
89         }
90         // account migration is not considered a critical operation, so succeed anyway
91         success();
92     }
93 
94     @Override
getStatusMsgId()95     public int getStatusMsgId() {
96         return R.string.progress_finishing_touches;
97     }
98 
99     @Override
getMetricsCategory()100     protected int getMetricsCategory() {
101         return PROVISIONING_COPY_ACCOUNT_TASK_MS;
102     }
103 
104     @VisibleForTesting
maybeCopyAccount(int targetUserId)105     boolean maybeCopyAccount(int targetUserId) {
106         Account accountToMigrate = mProvisioningParams.accountToMigrate;
107         UserHandle sourceUser = UserHandle.of(mSourceUserId);
108         UserHandle targetUser = UserHandle.of(targetUserId);
109 
110         if (accountToMigrate == null) {
111             ProvisionLogger.logd("No account to migrate.");
112             return false;
113         }
114         if (sourceUser.equals(targetUser)) {
115             ProvisionLogger.loge("sourceUser and targetUser are the same, won't migrate account.");
116             return false;
117         }
118         ProvisionLogger.logd("Attempting to copy account from " + sourceUser + " to " + targetUser);
119         try {
120             AccountManager accountManager = (AccountManager)
121                     mContext.getSystemService(Context.ACCOUNT_SERVICE);
122             boolean copySucceeded = accountManager.copyAccountToUser(
123                     accountToMigrate,
124                     sourceUser,
125                     targetUser,
126                     /* callback= */ null, /* handler= */ null)
127                     .getResult(ACCOUNT_COPY_TIMEOUT_SECONDS, TimeUnit.SECONDS);
128             if (copySucceeded) {
129                 ProvisionLogger.logi("Copied account to " + targetUser);
130                 mProvisioningAnalyticsTracker.logCopyAccountStatus(mContext,
131                         COPY_ACCOUNT_SUCCEEDED);
132                 return true;
133             } else {
134                 mProvisioningAnalyticsTracker.logCopyAccountStatus(mContext, COPY_ACCOUNT_FAILED);
135                 ProvisionLogger.loge("Could not copy account to " + targetUser);
136             }
137         } catch (OperationCanceledException e) {
138             mProvisioningAnalyticsTracker.logCopyAccountStatus(mContext, COPY_ACCOUNT_TIMED_OUT);
139             ProvisionLogger.loge("Exception copying account to " + targetUser, e);
140         } catch (AuthenticatorException | IOException e) {
141             mProvisioningAnalyticsTracker.logCopyAccountStatus(mContext, COPY_ACCOUNT_EXCEPTION);
142             ProvisionLogger.loge("Exception copying account to " + targetUser, e);
143         }
144         return false;
145     }
146 }
147