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