blob: 0f953180f8064aeaae8f0e40e0b411a7b897b375 [file] [log] [blame]
Christopher Tate487529a2009-04-29 14:03:25 -07001/*
2 * Copyright (C) 2009 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.server;
18
19import android.backup.BackupService;
20import android.backup.IBackupService;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.content.pm.ServiceInfo;
28import android.os.Binder;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.Message;
32import android.os.RemoteException;
33import android.util.Log;
34import android.util.SparseArray;
35
36import android.backup.IBackupManager;
37
38import java.lang.String;
39import java.util.HashSet;
40import java.util.List;
41
42class BackupManagerService extends IBackupManager.Stub {
43 private static final String TAG = "BackupManagerService";
44 private static final boolean DEBUG = true;
45
46 private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
47
48 private static final int MSG_RUN_BACKUP = 1;
49
50 private Context mContext;
51 private PackageManager mPackageManager;
52 private final BackupHandler mBackupHandler = new BackupHandler();
53 // map UIDs to the set of backup client services within that UID's app set
54 private SparseArray<HashSet<ServiceInfo>> mBackupParticipants
55 = new SparseArray<HashSet<ServiceInfo>>();
56 // set of backup services that have pending changes
57 private HashSet<ServiceInfo> mPendingBackups = new HashSet<ServiceInfo>();
58 private final Object mQueueLock = new Object();
59
60
61 // ----- Handler that runs the actual backup process asynchronously -----
62
63 private class BackupHandler extends Handler implements ServiceConnection {
64 private volatile Object mBindSignaller = new Object();
65 private volatile boolean mBinding = false;
66 private IBackupService mTargetService = null;
67
68 public void handleMessage(Message msg) {
69
70 switch (msg.what) {
71 case MSG_RUN_BACKUP:
72 {
73 // snapshot the pending-backup set and work on that
74 HashSet<ServiceInfo> queue;
75 synchronized (mQueueLock) {
76 queue = mPendingBackups;
77 mPendingBackups = new HashSet<ServiceInfo>();
78 // !!! TODO: start a new backup-queue journal file too
79 }
80
81 // Walk the set of pending backups, setting up the relevant files and
82 // invoking the backup service in each participant
83 Intent backupIntent = new Intent(BackupService.SERVICE_ACTION);
84 for (ServiceInfo service : queue) {
85 mBinding = true;
86 mTargetService = null;
87
88 backupIntent.setClassName(service.packageName, service.name);
89 Log.d(TAG, "binding to " + backupIntent);
90 if (mContext.bindService(backupIntent, this, 0)) {
91 synchronized (mBindSignaller) {
92 while (mTargetService == null && mBinding == true) {
93 try {
94 mBindSignaller.wait();
95 } catch (InterruptedException e) {
96 }
97 }
98 }
99 if (mTargetService != null) {
100 try {
101 Log.d(TAG, "invoking doBackup() on " + backupIntent);
102 // !!! TODO: set up files
103 mTargetService.doBackup(-1, -1, -1);
104 } catch (RemoteException e) {
105 Log.d(TAG, "Remote target " + backupIntent
106 + " threw during backup:");
107 e.printStackTrace();
108 }
109 mContext.unbindService(this);
110 }
111 } else {
112 Log.d(TAG, "Unable to bind to " + backupIntent);
113 }
114 }
115 }
116 break;
117 }
118 }
119
120 public void onServiceConnected(ComponentName name, IBinder service) {
121 synchronized (mBindSignaller) {
122 mTargetService = IBackupService.Stub.asInterface(service);
123 mBinding = false;
124 mBindSignaller.notifyAll();
125 }
126 }
127
128 public void onServiceDisconnected(ComponentName name) {
129 synchronized (mBindSignaller) {
130 mTargetService = null;
131 mBinding = false;
132 mBindSignaller.notifyAll();
133 }
134 }
135 }
136
137 public BackupManagerService(Context context) {
138 mContext = context;
139 mPackageManager = context.getPackageManager();
140
141 // Identify the backup participants
142 // !!! TODO: also watch package-install to keep this up to date
143 List<ResolveInfo> services = mPackageManager.queryIntentServices(
144 new Intent(BackupService.SERVICE_ACTION), 0);
145 if (DEBUG) {
146 Log.v(TAG, "Backup participants: " + services.size());
147 for (ResolveInfo ri : services) {
148 Log.v(TAG, " " + ri + " : " + ri.filter);
149 }
150 }
151
152 // Build our mapping of uid to backup client services
153 for (ResolveInfo ri : services) {
154 int uid = ri.serviceInfo.applicationInfo.uid;
155 HashSet<ServiceInfo> set = mBackupParticipants.get(uid);
156 if (set == null) {
157 set = new HashSet<ServiceInfo>();
158 mBackupParticipants.put(uid, set);
159 }
160 set.add(ri.serviceInfo);
161 }
162 }
163
164
165 // ----- IBackupManager binder interface -----
166
Christopher Tatea8bf8152009-04-30 11:36:21 -0700167 public void dataChanged(String packageName) throws RemoteException {
Christopher Tate487529a2009-04-29 14:03:25 -0700168 // Record that we need a backup pass for the caller. Since multiple callers
169 // may share a uid, we need to note all candidates within that uid and schedule
170 // a backup pass for each of them.
171
172 HashSet<ServiceInfo> targets = mBackupParticipants.get(Binder.getCallingUid());
173 if (targets != null) {
174 synchronized (mQueueLock) {
175 // Note that this client has made data changes that need to be backed up
Christopher Tate487529a2009-04-29 14:03:25 -0700176 for (ServiceInfo service : targets) {
Christopher Tatea8bf8152009-04-30 11:36:21 -0700177 // validate the caller-supplied package name against the known set of
178 // packages associated with this uid
179 if (service.packageName.equals(packageName)) {
180 // add the caller to the set of pending backups
181 if (mPendingBackups.add(service)) {
182 // !!! TODO: write to the pending-backup journal file in case of crash
183 }
Christopher Tate487529a2009-04-29 14:03:25 -0700184 }
185 }
186
187 // Schedule a backup pass in a few minutes. As backup-eligible data
188 // keeps changing, continue to defer the backup pass until things
189 // settle down, to avoid extra overhead.
190 mBackupHandler.removeMessages(MSG_RUN_BACKUP);
191 mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL);
192 }
193 }
194 }
195}