1 /* 2 * Copyright (C) 2023 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.server.healthconnect.migration; 18 19 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_ALLOWED_ERROR; 20 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_ALLOWED_MIGRATOR_DISABLED; 21 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_ALLOWED_NOT_STARTED; 22 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_ALLOWED_PAUSED; 23 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_APP_UPGRADE_REQUIRED; 24 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_COMPLETE; 25 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_COMPLETE_IDLE; 26 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_IDLE; 27 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_IN_PROGRESS; 28 import static android.health.connect.migration.HealthConnectMigrationUiState.MIGRATION_UI_STATE_MODULE_UPGRADE_REQUIRED; 29 30 import android.content.Context; 31 import android.health.connect.HealthConnectDataState; 32 import android.health.connect.migration.HealthConnectMigrationUiState; 33 import android.os.UserHandle; 34 35 import com.android.server.healthconnect.migration.notification.MigrationNotificationSender; 36 37 /** 38 * Sends the appropriate notification based on the migration state, and returns the UI state to the 39 * caller. 40 * 41 * @hide 42 */ 43 public class MigrationUiStateManager { 44 45 private final Context mContext; 46 private volatile UserHandle mUserHandle; 47 private final MigrationNotificationSender mMigrationNotificationSender; 48 private final MigrationStateManager mMigrationStateManager; 49 private static final String TAG = "MigrationUiStateManager"; 50 MigrationUiStateManager( Context context, UserHandle userHandle, MigrationStateManager migrationStateManager, MigrationNotificationSender migrationNotificationSender)51 public MigrationUiStateManager( 52 Context context, 53 UserHandle userHandle, 54 MigrationStateManager migrationStateManager, 55 MigrationNotificationSender migrationNotificationSender) { 56 this.mContext = context; 57 this.mUserHandle = userHandle; 58 this.mMigrationNotificationSender = migrationNotificationSender; 59 this.mMigrationStateManager = migrationStateManager; 60 } 61 62 /** Assigns a new user handle to this object. */ setupForUser(UserHandle userHandle)63 public void setupForUser(UserHandle userHandle) { 64 this.mUserHandle = userHandle; 65 } 66 67 /** Attaches this MigrationUiStateManager to the provided {@link MigrationStateManager}. */ attachTo(MigrationStateManager migrationStateManager)68 public void attachTo(MigrationStateManager migrationStateManager) { 69 migrationStateManager.addStateChangedListener(this::onMigrationStateChanged); 70 } 71 72 /** Returns the current Migration UI state. */ 73 @HealthConnectMigrationUiState.Type getHealthConnectMigrationUiState()74 public int getHealthConnectMigrationUiState() { 75 // get current migration state 76 @HealthConnectDataState.DataMigrationState 77 int migrationState = mMigrationStateManager.getMigrationState(); 78 79 switch (migrationState) { 80 case HealthConnectDataState.MIGRATION_STATE_IDLE: 81 return MIGRATION_UI_STATE_IDLE; 82 83 case HealthConnectDataState.MIGRATION_STATE_APP_UPGRADE_REQUIRED: 84 return MIGRATION_UI_STATE_APP_UPGRADE_REQUIRED; 85 86 case HealthConnectDataState.MIGRATION_STATE_MODULE_UPGRADE_REQUIRED: 87 return MIGRATION_UI_STATE_MODULE_UPGRADE_REQUIRED; 88 89 case HealthConnectDataState.MIGRATION_STATE_ALLOWED: 90 if (isUiStateAllowedPaused()) { 91 return MIGRATION_UI_STATE_ALLOWED_NOT_STARTED; 92 } else if (isMigratorDisabled()) { 93 return MIGRATION_UI_STATE_ALLOWED_ERROR; 94 } else if (isUiStateInProgressPaused()) { 95 return MIGRATION_UI_STATE_ALLOWED_PAUSED; 96 } else { 97 return MIGRATION_UI_STATE_ALLOWED_MIGRATOR_DISABLED; 98 } 99 100 case HealthConnectDataState.MIGRATION_STATE_IN_PROGRESS: 101 return MIGRATION_UI_STATE_IN_PROGRESS; 102 103 case HealthConnectDataState.MIGRATION_STATE_COMPLETE: 104 if (isUiStateCompleteFromIdle()) { 105 return MIGRATION_UI_STATE_COMPLETE_IDLE; 106 } else { 107 return MIGRATION_UI_STATE_COMPLETE; 108 } 109 110 default: 111 throw new IllegalArgumentException( 112 "Cannot compute migration UI state. Unknown migration state: " 113 + migrationState); 114 } 115 } 116 onMigrationStateChanged(@ealthConnectDataState.DataMigrationState int newState)117 private void onMigrationStateChanged(@HealthConnectDataState.DataMigrationState int newState) { 118 119 @HealthConnectMigrationUiState.Type 120 int migrationUiState = getHealthConnectMigrationUiState(); 121 122 switch (migrationUiState) { 123 case MIGRATION_UI_STATE_ALLOWED_PAUSED: 124 mMigrationNotificationSender.sendNotification( 125 MigrationNotificationSender.NOTIFICATION_TYPE_MIGRATION_PAUSED, 126 mUserHandle); 127 break; 128 129 case MIGRATION_UI_STATE_MODULE_UPGRADE_REQUIRED: 130 mMigrationNotificationSender.sendNotification( 131 MigrationNotificationSender 132 .NOTIFICATION_TYPE_MIGRATION_MODULE_UPDATE_NEEDED, 133 mUserHandle); 134 break; 135 default: 136 mMigrationNotificationSender.clearNotifications(mUserHandle); 137 break; 138 } 139 } 140 isUiStateCompleteFromIdle()141 private boolean isUiStateCompleteFromIdle() { 142 return mMigrationStateManager.hasIdleStateTimedOut(); 143 } 144 isUiStateAllowedPaused()145 private boolean isUiStateAllowedPaused() { 146 // Migrator not responding to broadcast before migration started 147 int migrationStartsCount = mMigrationStateManager.getMigrationStartsCount(); 148 boolean isApkInstalled = 149 mMigrationStateManager.existsMigrationAwarePackage(mContext) 150 && mMigrationStateManager.existsMigratorPackage(mContext); 151 boolean doesMigratorHandleInfoIntent = 152 mMigrationStateManager.doesMigratorHandleInfoIntent(mContext); 153 154 return migrationStartsCount == 0 && isApkInstalled && doesMigratorHandleInfoIntent; 155 } 156 isMigratorDisabled()157 private boolean isMigratorDisabled() { 158 int migrationStartsCount = mMigrationStateManager.getMigrationStartsCount(); 159 boolean doesMigratorHandleInfoIntent = 160 mMigrationStateManager.doesMigratorHandleInfoIntent(mContext); 161 162 return migrationStartsCount > 0 && !doesMigratorHandleInfoIntent; 163 } 164 isUiStateInProgressPaused()165 private boolean isUiStateInProgressPaused() { 166 int migrationStartsCount = mMigrationStateManager.getMigrationStartsCount(); 167 boolean doesMigratorHandleInfoIntent = 168 mMigrationStateManager.doesMigratorHandleInfoIntent(mContext); 169 boolean hasInProgressStateTimedOut = mMigrationStateManager.hasInProgressStateTimedOut(); 170 171 return migrationStartsCount > 0 172 && doesMigratorHandleInfoIntent 173 && hasInProgressStateTimedOut; 174 } 175 } 176