1 /* 2 * Copyright 2019, 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.provisioning; 18 19 import android.app.Activity; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.os.Handler; 24 import android.os.HandlerThread; 25 import android.os.Looper; 26 import android.util.Pair; 27 28 import com.android.internal.annotations.GuardedBy; 29 import com.android.managedprovisioning.common.Globals; 30 import com.android.managedprovisioning.common.ProvisionLogger; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 35 /** 36 * Helper class for ProvisioningManager. 37 */ 38 // TODO(b/123288153): Rearrange provisioning activity, manager, controller classes. 39 public class ProvisioningManagerHelper { 40 41 private static final int CALLBACK_NONE = 0; 42 private static final int CALLBACK_ERROR = 1; 43 private static final int CALLBACK_PRE_FINALIZED = 2; 44 45 private static final Intent SERVICE_INTENT = new Intent().setComponent(new ComponentName( 46 Globals.MANAGED_PROVISIONING_PACKAGE_NAME, 47 ProvisioningService.class.getName())); 48 49 private final Context mContext; 50 private final Handler mUiHandler; 51 52 @GuardedBy("this") 53 private List<ProvisioningManagerCallback> mCallbacks = new ArrayList<>(); 54 55 private int mLastCallback = CALLBACK_NONE; 56 private Pair<Pair<Integer, Integer>, Boolean> mLastError; // TODO: refactor 57 private HandlerThread mHandlerThread; 58 ProvisioningManagerHelper(Context context)59 public ProvisioningManagerHelper(Context context) { 60 mContext = context; 61 mUiHandler = new Handler(Looper.getMainLooper()); 62 } 63 startNewProvisioningLocked(AbstractProvisioningController controller)64 public void startNewProvisioningLocked(AbstractProvisioningController controller) { 65 if (mHandlerThread == null) { 66 mHandlerThread = new HandlerThread( 67 String.format("%s Worker", controller.getClass().getName())); 68 mHandlerThread.start(); 69 mContext.startService(SERVICE_INTENT); 70 } 71 mLastCallback = CALLBACK_NONE; 72 mLastError = null; 73 74 controller.start(mHandlerThread.getLooper()); 75 } 76 registerListener(ProvisioningManagerCallback callback)77 public void registerListener(ProvisioningManagerCallback callback) { 78 synchronized (this) { 79 mCallbacks.add(callback); 80 callLastCallbackLocked(callback); 81 } 82 } 83 unregisterListener(ProvisioningManagerCallback callback)84 public void unregisterListener(ProvisioningManagerCallback callback) { 85 synchronized (this) { 86 mCallbacks.remove(callback); 87 } 88 } 89 error(int titleId, int messageId, boolean factoryResetRequired)90 public void error(int titleId, int messageId, boolean factoryResetRequired) { 91 synchronized (this) { 92 for (ProvisioningManagerCallback callback : mCallbacks) { 93 postCallbackToUiHandler(callback, () -> { 94 callback.error(titleId, messageId, factoryResetRequired); 95 }); 96 } 97 mLastCallback = CALLBACK_ERROR; 98 mLastError = Pair.create(Pair.create(titleId, messageId), factoryResetRequired); 99 } 100 } 101 callLastCallbackLocked(ProvisioningManagerCallback callback)102 private void callLastCallbackLocked(ProvisioningManagerCallback callback) { 103 switch (mLastCallback) { 104 case CALLBACK_ERROR: 105 final Pair<Pair<Integer, Integer>, Boolean> error = mLastError; 106 postCallbackToUiHandler(callback, () -> { 107 callback.error(error.first.first, error.first.second, error.second); 108 }); 109 break; 110 case CALLBACK_PRE_FINALIZED: 111 postCallbackToUiHandler(callback, callback::preFinalizationCompleted); 112 break; 113 default: 114 ProvisionLogger.logd("No previous callback"); 115 } 116 } 117 cancelProvisioning(AbstractProvisioningController controller)118 public boolean cancelProvisioning(AbstractProvisioningController controller) { 119 synchronized (this) { 120 if (controller != null) { 121 controller.cancel(); 122 return true; 123 } else { 124 ProvisionLogger.loge("Trying to cancel provisioning, but controller is null"); 125 return false; 126 } 127 } 128 } 129 notifyPreFinalizationCompleted()130 public void notifyPreFinalizationCompleted() { 131 synchronized (this) { 132 for (ProvisioningManagerCallback callback : mCallbacks) { 133 postCallbackToUiHandler(callback, callback::preFinalizationCompleted); 134 } 135 mLastCallback = CALLBACK_PRE_FINALIZED; 136 } 137 } 138 clearResourcesLocked()139 public void clearResourcesLocked() { 140 if (mHandlerThread != null) { 141 mHandlerThread.quitSafely(); 142 mHandlerThread = null; 143 mContext.stopService(SERVICE_INTENT); 144 } 145 } 146 147 /** 148 * Executes the callback method on the main thread. 149 * 150 * <p>Inside the main thread, we have to first verify the callback is still present on the 151 * callbacks list. This is because when a config change happens (e.g. a different locale was 152 * specified), {@link ProvisioningActivity} is recreated and the old 153 * {@link ProvisioningActivity} instance is left in a bad state. Any callbacks posted before 154 * this happens will still be executed. Fixes b/131719633. 155 */ postCallbackToUiHandler(ProvisioningManagerCallback callback, Runnable callbackRunnable)156 private void postCallbackToUiHandler(ProvisioningManagerCallback callback, 157 Runnable callbackRunnable) { 158 mUiHandler.post(() -> { 159 synchronized (ProvisioningManagerHelper.this) { 160 if (isCallbackStillRequired(callback)) { 161 callbackRunnable.run(); 162 } 163 } 164 }); 165 } 166 isCallbackStillRequired(ProvisioningManagerCallback callback)167 private boolean isCallbackStillRequired(ProvisioningManagerCallback callback) { 168 return mCallbacks.contains(callback); 169 } 170 } 171