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