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