BackupRestoreConfirmation.java revision 728a1c4d5ed3b808172013a7f5bb5065d1e964f6
1/* 2 * Copyright (C) 2011 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 17package com.android.backupconfirm; 18 19import android.app.Activity; 20import android.app.backup.FullBackup; 21import android.app.backup.IBackupManager; 22import android.app.backup.IFullBackupRestoreObserver; 23import android.content.Context; 24import android.content.Intent; 25import android.os.Bundle; 26import android.os.Handler; 27import android.os.Message; 28import android.os.RemoteException; 29import android.os.ServiceManager; 30import android.util.Slog; 31import android.view.View; 32import android.widget.Button; 33import android.widget.TextView; 34import android.widget.Toast; 35 36/** 37 * Confirm with the user that a requested full backup/restore operation is legitimate. 38 * Any attempt to perform a full backup/restore will launch this UI and wait for a 39 * designated timeout interval (nominally 30 seconds) for the user to confirm. If the 40 * user fails to respond within the timeout period, or explicitly refuses the operation 41 * within the UI presented here, no data will be transferred off the device. 42 * 43 * Note that the fully scoped name of this class is baked into the backup manager service. 44 * 45 * @hide 46 */ 47public class BackupRestoreConfirmation extends Activity { 48 static final String TAG = "BackupRestoreConfirmation"; 49 static final boolean DEBUG = true; 50 51 static final int MSG_START_BACKUP = 1; 52 static final int MSG_BACKUP_PACKAGE = 2; 53 static final int MSG_END_BACKUP = 3; 54 static final int MSG_START_RESTORE = 11; 55 static final int MSG_RESTORE_PACKAGE = 12; 56 static final int MSG_END_RESTORE = 13; 57 static final int MSG_TIMEOUT = 100; 58 59 Handler mHandler; 60 IBackupManager mBackupManager; 61 FullObserver mObserver; 62 int mToken; 63 boolean mDidAcknowledge; 64 65 TextView mStatusView; 66 TextView mCurPassword; 67 TextView mEncPassword; 68 Button mAllowButton; 69 Button mDenyButton; 70 71 // Handler for dealing with observer callbacks on the main thread 72 class ObserverHandler extends Handler { 73 Context mContext; 74 ObserverHandler(Context context) { 75 mContext = context; 76 mDidAcknowledge = false; 77 } 78 79 @Override 80 public void handleMessage(Message msg) { 81 switch (msg.what) { 82 case MSG_START_BACKUP: { 83 Toast.makeText(mContext, "!!! Backup starting !!!", Toast.LENGTH_LONG).show(); 84 } 85 break; 86 87 case MSG_BACKUP_PACKAGE: { 88 String name = (String) msg.obj; 89 mStatusView.setText(name); 90 } 91 break; 92 93 case MSG_END_BACKUP: { 94 Toast.makeText(mContext, "!!! Backup ended !!!", Toast.LENGTH_SHORT).show(); 95 finish(); 96 } 97 break; 98 99 case MSG_START_RESTORE: { 100 Toast.makeText(mContext, "!!! Restore starting !!!", Toast.LENGTH_LONG).show(); 101 } 102 break; 103 104 case MSG_RESTORE_PACKAGE: { 105 String name = (String) msg.obj; 106 mStatusView.setText(name); 107 } 108 break; 109 110 case MSG_END_RESTORE: { 111 Toast.makeText(mContext, "!!! Restore ended !!!", Toast.LENGTH_SHORT).show(); 112 finish(); 113 } 114 break; 115 116 case MSG_TIMEOUT: { 117 Toast.makeText(mContext, "!!! TIMED OUT !!!", Toast.LENGTH_LONG).show(); 118 } 119 break; 120 } 121 } 122 } 123 124 @Override 125 public void onCreate(Bundle icicle) { 126 super.onCreate(icicle); 127 128 final Intent intent = getIntent(); 129 final String action = intent.getAction(); 130 131 final int layoutId; 132 if (action.equals(FullBackup.FULL_BACKUP_INTENT_ACTION)) { 133 layoutId = R.layout.confirm_backup; 134 } else if (action.equals(FullBackup.FULL_RESTORE_INTENT_ACTION)) { 135 layoutId = R.layout.confirm_restore; 136 } else { 137 Slog.w(TAG, "Backup/restore confirmation activity launched with invalid action!"); 138 finish(); 139 return; 140 } 141 142 mToken = intent.getIntExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, -1); 143 if (mToken < 0) { 144 Slog.e(TAG, "Backup/restore confirmation requested but no token passed!"); 145 finish(); 146 return; 147 } 148 149 mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE)); 150 151 mHandler = new ObserverHandler(getApplicationContext()); 152 mObserver = new FullObserver(); 153 154 setContentView(layoutId); 155 156 // Same resource IDs for each layout variant (backup / restore) 157 mStatusView = (TextView) findViewById(R.id.package_name); 158 mAllowButton = (Button) findViewById(R.id.button_allow); 159 mDenyButton = (Button) findViewById(R.id.button_deny); 160 161 mCurPassword = (TextView) findViewById(R.id.password); 162 mEncPassword = (TextView) findViewById(R.id.enc_password); 163 TextView curPwDesc = (TextView) findViewById(R.id.password_desc); 164 165 // We vary the password prompt depending on whether one is predefined 166 if (!haveBackupPassword()) { 167 curPwDesc.setVisibility(View.GONE); 168 mCurPassword.setVisibility(View.GONE); 169 if (layoutId == R.layout.confirm_backup) { 170 TextView encPwDesc = (TextView) findViewById(R.id.enc_password_desc); 171 encPwDesc.setText(R.string.backup_enc_password_optional); 172 } 173 } 174 175 mAllowButton.setOnClickListener(new View.OnClickListener() { 176 @Override 177 public void onClick(View v) { 178 sendAcknowledgement(mToken, true, mObserver); 179 mAllowButton.setEnabled(false); 180 mDenyButton.setEnabled(false); 181 } 182 }); 183 184 mDenyButton.setOnClickListener(new View.OnClickListener() { 185 @Override 186 public void onClick(View v) { 187 sendAcknowledgement(mToken, false, mObserver); 188 mAllowButton.setEnabled(false); 189 mDenyButton.setEnabled(false); 190 } 191 }); 192 } 193 194 @Override 195 public void onStop() { 196 super.onStop(); 197 198 // We explicitly equate departure from the UI with refusal. This includes the 199 // implicit configuration-changed stop/restart cycle. 200 sendAcknowledgement(mToken, false, null); 201 finish(); 202 } 203 204 void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver observer) { 205 if (!mDidAcknowledge) { 206 mDidAcknowledge = true; 207 208 try { 209 mBackupManager.acknowledgeFullBackupOrRestore(mToken, 210 allow, 211 String.valueOf(mCurPassword.getText()), 212 String.valueOf(mEncPassword.getText()), 213 mObserver); 214 } catch (RemoteException e) { 215 // TODO: bail gracefully if we can't contact the backup manager 216 } 217 } 218 } 219 220 boolean haveBackupPassword() { 221 try { 222 return mBackupManager.hasBackupPassword(); 223 } catch (RemoteException e) { 224 return true; // in the failure case, assume we need one 225 } 226 } 227 228 /** 229 * The observer binder for showing backup/restore progress. This binder just bounces 230 * the notifications onto the main thread. 231 */ 232 class FullObserver extends IFullBackupRestoreObserver.Stub { 233 // 234 // IFullBackupRestoreObserver implementation 235 // 236 @Override 237 public void onStartBackup() throws RemoteException { 238 mHandler.sendEmptyMessage(MSG_START_BACKUP); 239 } 240 241 @Override 242 public void onBackupPackage(String name) throws RemoteException { 243 mHandler.sendMessage(mHandler.obtainMessage(MSG_BACKUP_PACKAGE, name)); 244 } 245 246 @Override 247 public void onEndBackup() throws RemoteException { 248 mHandler.sendEmptyMessage(MSG_END_BACKUP); 249 } 250 251 @Override 252 public void onStartRestore() throws RemoteException { 253 mHandler.sendEmptyMessage(MSG_START_RESTORE); 254 } 255 256 @Override 257 public void onRestorePackage(String name) throws RemoteException { 258 mHandler.sendMessage(mHandler.obtainMessage(MSG_RESTORE_PACKAGE, name)); 259 } 260 261 @Override 262 public void onEndRestore() throws RemoteException { 263 mHandler.sendEmptyMessage(MSG_END_RESTORE); 264 } 265 266 @Override 267 public void onTimeout() throws RemoteException { 268 mHandler.sendEmptyMessage(MSG_TIMEOUT); 269 } 270 } 271} 272