blob: bd8b8ef87b78d2067be823a4f2f1c5861efea626 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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.app.AlarmManager;
20import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070021import android.appwidget.AppWidgetManager;
22import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.ActivityInfo;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageInfo;
31import android.content.pm.ResolveInfo;
32import android.content.pm.PackageItemInfo;
33import android.content.res.TypedArray;
34import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.net.Uri;
36import android.os.Binder;
37import android.os.Bundle;
Marco Nelissen54796e72009-04-30 15:16:30 -070038import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.os.RemoteException;
40import android.os.SystemClock;
41import android.util.AttributeSet;
42import android.util.Log;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070043import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.util.Xml;
45import android.widget.RemoteViews;
46
47import java.io.IOException;
48import java.io.File;
49import java.io.FileDescriptor;
50import java.io.FileInputStream;
51import java.io.FileOutputStream;
52import java.io.PrintWriter;
53import java.util.ArrayList;
54import java.util.List;
55import java.util.HashMap;
56import java.util.HashSet;
57
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070058import com.android.internal.appwidget.IAppWidgetService;
59import com.android.internal.appwidget.IAppWidgetHost;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import com.android.internal.util.XmlUtils;
61import com.android.internal.util.FastXmlSerializer;
62
63import org.xmlpull.v1.XmlPullParser;
64import org.xmlpull.v1.XmlPullParserException;
65import org.xmlpull.v1.XmlSerializer;
66
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070067class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070069 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070071 private static final String SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
73
74 /*
75 * When identifying a Host or Provider based on the calling process, use the uid field.
76 * When identifying a Host or Provider based on a package manager broadcast, use the
77 * package given.
78 */
79
80 static class Provider {
81 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070082 AppWidgetProviderInfo info;
83 ArrayList<AppWidgetId> instances = new ArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 PendingIntent broadcast;
85 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
86
87 int tag; // for use while saving state (the index)
88 }
89
90 static class Host {
91 int uid;
92 int hostId;
93 String packageName;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070094 ArrayList<AppWidgetId> instances = new ArrayList();
95 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
97
98 int tag; // for use while saving state (the index)
99 }
100
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700101 static class AppWidgetId {
102 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 Provider provider;
104 RemoteViews views;
105 Host host;
106 }
107
108 Context mContext;
109 PackageManager mPackageManager;
110 AlarmManager mAlarmManager;
111 ArrayList<Provider> mInstalledProviders = new ArrayList();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700112 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
113 ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 ArrayList<Host> mHosts = new ArrayList();
115 boolean mSafeMode;
116
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700117 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 mContext = context;
119 mPackageManager = context.getPackageManager();
120 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
121 }
122
123 public void systemReady(boolean safeMode) {
124 mSafeMode = safeMode;
125
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700126 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 loadStateLocked();
128
129 // Register for the boot completed broadcast, so we can send the
130 // ENABLE broacasts. If we try to send them now, they time out,
131 // because the system isn't ready to handle them yet.
132 mContext.registerReceiver(mBroadcastReceiver,
133 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
134
135 // Register for broadcasts about package install, etc., so we can
136 // update the provider list.
137 IntentFilter filter = new IntentFilter();
138 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
139 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
140 filter.addDataScheme("package");
141 mContext.registerReceiver(mBroadcastReceiver, filter);
142 }
143
144 @Override
145 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
146 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
147 != PackageManager.PERMISSION_GRANTED) {
148 pw.println("Permission Denial: can't dump from from pid="
149 + Binder.getCallingPid()
150 + ", uid=" + Binder.getCallingUid());
151 return;
152 }
153
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700154 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700156 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 for (int i=0; i<N; i++) {
158 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700159 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700160 pw.print(" ["); pw.print(i); pw.print("] provider ");
161 pw.print(info.provider.flattenToShortString());
162 pw.println(':');
163 pw.print(" min=("); pw.print(info.minWidth);
164 pw.print("x"); pw.print(info.minHeight);
165 pw.print(") updatePeriodMillis=");
166 pw.print(info.updatePeriodMillis);
167 pw.print(" initialLayout=#");
168 pw.print(Integer.toHexString(info.initialLayout));
169 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 }
171
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700172 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700173 pw.println(" ");
174 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700176 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700177 pw.print(" ["); pw.print(i); pw.print("] id=");
178 pw.println(id.appWidgetId);;
179 pw.print(" hostId=");
180 pw.print(id.host.hostId); pw.print(' ');
181 pw.print(id.host.packageName); pw.print('/');
182 pw.println(id.host.uid);
183 if (id.provider != null) {
184 pw.print(" provider=");
185 pw.println(id.provider.info.provider.flattenToShortString());
186 }
187 if (id.host != null) {
188 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
189 }
190 if (id.views != null) {
191 pw.print(" views="); pw.println(id.views);
192 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 }
194
195 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700196 pw.println(" ");
197 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 for (int i=0; i<N; i++) {
199 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700200 pw.print(" ["); pw.print(i); pw.print("] hostId=");
201 pw.print(host.hostId); pw.print(' ');
202 pw.print(host.packageName); pw.print('/');
203 pw.print(host.uid); pw.println(':');
204 pw.print(" callbacks="); pw.println(host.callbacks);
205 pw.print(" instances.size="); pw.print(host.instances.size());
206 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 }
208 }
209 }
210
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700211 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700213 synchronized (mAppWidgetIds) {
214 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215
216 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
217
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700218 AppWidgetId id = new AppWidgetId();
219 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 id.host = host;
221
222 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700223 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224
225 saveStateLocked();
226
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700227 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 }
229 }
230
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700231 public void deleteAppWidgetId(int appWidgetId) {
232 synchronized (mAppWidgetIds) {
233 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700235 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 saveStateLocked();
237 }
238 }
239 }
240
241 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700242 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 int callingUid = getCallingUid();
244 Host host = lookupHostLocked(callingUid, hostId);
245 if (host != null) {
246 deleteHostLocked(host);
247 saveStateLocked();
248 }
249 }
250 }
251
252 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700253 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 int callingUid = getCallingUid();
255 final int N = mHosts.size();
256 boolean changed = false;
257 for (int i=N-1; i>=0; i--) {
258 Host host = mHosts.get(i);
259 if (host.uid == callingUid) {
260 deleteHostLocked(host);
261 changed = true;
262 }
263 }
264 if (changed) {
265 saveStateLocked();
266 }
267 }
268 }
269
270 void deleteHostLocked(Host host) {
271 final int N = host.instances.size();
272 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700273 AppWidgetId id = host.instances.get(i);
274 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 }
276 host.instances.clear();
277 mHosts.remove(host);
278 // it's gone or going away, abruptly drop the callback connection
279 host.callbacks = null;
280 }
281
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700282 void deleteAppWidgetLocked(AppWidgetId id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 Host host = id.host;
284 host.instances.remove(id);
285 pruneHostLocked(host);
286
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700287 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288
289 Provider p = id.provider;
290 if (p != null) {
291 p.instances.remove(id);
292 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700293 // send the broacast saying that this appWidgetId has been deleted
294 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700296 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 mContext.sendBroadcast(intent);
298 if (p.instances.size() == 0) {
299 // cancel the future updates
300 cancelBroadcasts(p);
301
302 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700303 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 intent.setComponent(p.info.provider);
305 mContext.sendBroadcast(intent);
306 }
307 }
308 }
309 }
310
311 void cancelBroadcasts(Provider p) {
312 if (p.broadcast != null) {
313 mAlarmManager.cancel(p.broadcast);
314 long token = Binder.clearCallingIdentity();
315 try {
316 p.broadcast.cancel();
317 } finally {
318 Binder.restoreCallingIdentity(token);
319 }
320 p.broadcast = null;
321 }
322 }
323
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700324 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
325 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
326 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
327 synchronized (mAppWidgetIds) {
328 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700330 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 }
332 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700333 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 + id.provider.info.provider);
335 }
336 Provider p = lookupProviderLocked(provider);
337 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700338 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 }
340 if (p.zombie) {
341 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
342 + " safe mode: " + provider);
343 }
344
345 id.provider = p;
346 p.instances.add(id);
347 int instancesSize = p.instances.size();
348 if (instancesSize == 1) {
349 // tell the provider that it's ready
350 sendEnableIntentLocked(p);
351 }
352
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700353 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 // It's less critical when the next one happens, so when we schdule the next one,
355 // we add updatePeriodMillis to its start time. That time will have some slop,
356 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700357 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358
359 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700360 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 saveStateLocked();
362 }
363 }
364
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700365 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
366 synchronized (mAppWidgetIds) {
367 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 if (id != null && id.provider != null && !id.provider.zombie) {
369 return id.provider.info;
370 }
371 return null;
372 }
373 }
374
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700375 public RemoteViews getAppWidgetViews(int appWidgetId) {
376 synchronized (mAppWidgetIds) {
377 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 if (id != null) {
379 return id.views;
380 }
381 return null;
382 }
383 }
384
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700385 public List<AppWidgetProviderInfo> getInstalledProviders() {
386 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 final int N = mInstalledProviders.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700388 ArrayList<AppWidgetProviderInfo> result = new ArrayList(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 for (int i=0; i<N; i++) {
390 Provider p = mInstalledProviders.get(i);
391 if (!p.zombie) {
392 result.add(p.info);
393 }
394 }
395 return result;
396 }
397 }
398
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700399 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
400 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 return;
402 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700403 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 return;
405 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700406 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700408 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700410 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
411 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 }
413 }
414 }
415
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700416 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
417 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 Provider p = lookupProviderLocked(provider);
419 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700420 Log.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 return;
422 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700423 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 final int N = instances.size();
425 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700426 AppWidgetId id = instances.get(i);
427 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 }
429 }
430 }
431
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700432 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
433 // allow for stale appWidgetIds and other badness
434 // lookup also checks that the calling process can access the appWidgetId
435 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
437 id.views = views;
438
439 // is anyone listening?
440 if (id.host.callbacks != null) {
441 try {
442 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700443 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 } catch (RemoteException e) {
445 // It failed; remove the callback. No need to prune because
446 // we know that this host is still referenced by this instance.
447 id.host.callbacks = null;
448 }
449 }
450 }
451 }
452
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700453 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 List<RemoteViews> updatedViews) {
455 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700456 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
458 host.callbacks = callbacks;
459
460 updatedViews.clear();
461
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700462 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 int N = instances.size();
464 int[] updatedIds = new int[N];
465 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700466 AppWidgetId id = instances.get(i);
467 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 updatedViews.add(id.views);
469 }
470 return updatedIds;
471 }
472 }
473
474 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700475 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 Host host = lookupHostLocked(getCallingUid(), hostId);
477 host.callbacks = null;
478 pruneHostLocked(host);
479 }
480 }
481
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700482 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700484 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 return true;
486 }
487 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700488 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 return true;
490 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700491 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700493 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 return true;
495 }
496 // Nobody else can access it.
497 return false;
498 }
499
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700500 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700502 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700504 AppWidgetId id = mAppWidgetIds.get(i);
505 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 return id;
507 }
508 }
509 return null;
510 }
511
512 Provider lookupProviderLocked(ComponentName provider) {
513 final int N = mInstalledProviders.size();
514 for (int i=0; i<N; i++) {
515 Provider p = mInstalledProviders.get(i);
516 if (p.info.provider.equals(provider)) {
517 return p;
518 }
519 }
520 return null;
521 }
522
523 Host lookupHostLocked(int uid, int hostId) {
524 final int N = mHosts.size();
525 for (int i=0; i<N; i++) {
526 Host h = mHosts.get(i);
527 if (h.uid == uid && h.hostId == hostId) {
528 return h;
529 }
530 }
531 return null;
532 }
533
534 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
535 final int N = mHosts.size();
536 for (int i=0; i<N; i++) {
537 Host h = mHosts.get(i);
538 if (h.hostId == hostId && h.packageName.equals(packageName)) {
539 return h;
540 }
541 }
542 Host host = new Host();
543 host.packageName = packageName;
544 host.uid = uid;
545 host.hostId = hostId;
546 mHosts.add(host);
547 return host;
548 }
549
550 void pruneHostLocked(Host host) {
551 if (host.instances.size() == 0 && host.callbacks == null) {
552 mHosts.remove(host);
553 }
554 }
555
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700556 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 PackageManager pm = mPackageManager;
558
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700559 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
561 PackageManager.GET_META_DATA);
562
563 final int N = broadcastReceivers.size();
564 for (int i=0; i<N; i++) {
565 ResolveInfo ri = broadcastReceivers.get(i);
566 addProviderLocked(ri);
567 }
568 }
569
570 boolean addProviderLocked(ResolveInfo ri) {
571 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
572 ri.activityInfo.name), ri);
573 if (p != null) {
574 mInstalledProviders.add(p);
575 return true;
576 } else {
577 return false;
578 }
579 }
580
581 void removeProviderLocked(int index, Provider p) {
582 int N = p.instances.size();
583 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700584 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700586 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 // Stop telling the host about updates for this from now on
588 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700589 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700591 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 id.provider = null;
593 pruneHostLocked(id.host);
594 id.host = null;
595 }
596 p.instances.clear();
597 mInstalledProviders.remove(index);
598 // no need to send the DISABLE broadcast, since the receiver is gone anyway
599 cancelBroadcasts(p);
600 }
601
602 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700603 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 intent.setComponent(p.info.provider);
605 mContext.sendBroadcast(intent);
606 }
607
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700608 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
609 if (appWidgetIds != null && appWidgetIds.length > 0) {
610 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
611 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 intent.setComponent(p.info.provider);
613 mContext.sendBroadcast(intent);
614 }
615 }
616
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700617 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 if (p.info.updatePeriodMillis > 0) {
619 // if this is the first instance, set the alarm. otherwise,
620 // rely on the fact that we've already set it and that
621 // PendingIntent.getBroadcast will update the extras.
622 boolean alreadyRegistered = p.broadcast != null;
623 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700624 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
625 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 intent.setComponent(p.info.provider);
627 long token = Binder.clearCallingIdentity();
628 try {
629 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
630 PendingIntent.FLAG_UPDATE_CURRENT);
631 } finally {
632 Binder.restoreCallingIdentity(token);
633 }
634 if (!alreadyRegistered) {
635 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
636 SystemClock.elapsedRealtime() + p.info.updatePeriodMillis,
637 p.info.updatePeriodMillis, p.broadcast);
638 }
639 }
640 }
641
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700642 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700644 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700646 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700648 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 }
650
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700651 public int[] getAppWidgetIds(ComponentName provider) {
652 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 Provider p = lookupProviderLocked(provider);
654 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700655 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 } else {
657 return new int[0];
658 }
659 }
660 }
661
662 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
663 Provider p = null;
664
665 ActivityInfo activityInfo = ri.activityInfo;
666 XmlResourceParser parser = null;
667 try {
668 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700669 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 if (parser == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700671 Log.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
672 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 return null;
674 }
675
676 AttributeSet attrs = Xml.asAttributeSet(parser);
677
678 int type;
679 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
680 && type != XmlPullParser.START_TAG) {
681 // drain whitespace, comments, etc.
682 }
683
684 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700685 if (!"appwidget-provider".equals(nodeName)) {
686 Log.w(TAG, "Meta-data does not start with appwidget-provider tag for"
687 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 return null;
689 }
690
691 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700692 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693
694 info.provider = component;
695 p.uid = activityInfo.applicationInfo.uid;
696
697 TypedArray sa = mContext.getResources().obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700698 com.android.internal.R.styleable.AppWidgetProviderInfo);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700699
700 // These dimensions has to be resolved in the application's context.
701 // We simply send back the raw complex data, which will be
702 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
703 TypedValue value = sa.peekValue(
704 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
705 info.minWidth = value != null ? value.data : 0;
706 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
707 info.minHeight = value != null ? value.data : 0;
708
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700710 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700712 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800713 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700714 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 if (className != null) {
716 info.configure = new ComponentName(component.getPackageName(), className);
717 }
718 info.label = activityInfo.loadLabel(mPackageManager).toString();
719 info.icon = ri.getIconResource();
720 sa.recycle();
721 } catch (Exception e) {
722 // Ok to catch Exception here, because anything going wrong because
723 // of what a client process passes to us should not be fatal for the
724 // system process.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700725 Log.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 return null;
727 } finally {
728 if (parser != null) parser.close();
729 }
730 return p;
731 }
732
733 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
734 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
735 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
736 throw new PackageManager.NameNotFoundException();
737 }
738 return pkgInfo.applicationInfo.uid;
739 }
740
741 int enforceCallingUid(String packageName) throws IllegalArgumentException {
742 int callingUid = getCallingUid();
743 int packageUid;
744 try {
745 packageUid = getUidForPackage(packageName);
746 } catch (PackageManager.NameNotFoundException ex) {
747 throw new IllegalArgumentException("packageName and uid don't match packageName="
748 + packageName);
749 }
Marco Nelissen54796e72009-04-30 15:16:30 -0700750 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 throw new IllegalArgumentException("packageName and uid don't match packageName="
752 + packageName);
753 }
754 return callingUid;
755 }
756
757 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700758 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 final int N = mInstalledProviders.size();
760 for (int i=0; i<N; i++) {
761 Provider p = mInstalledProviders.get(i);
762 if (p.instances.size() > 0) {
763 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700764 int[] appWidgetIds = getAppWidgetIds(p);
765 sendUpdateIntentLocked(p, appWidgetIds);
766 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 }
768 }
769 }
770 }
771
772 // only call from initialization -- it assumes that the data structures are all empty
773 void loadStateLocked() {
774 File temp = savedStateTempFile();
775 File real = savedStateRealFile();
776
777 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
778 // real one. if there is both a real file and a temp one, assume that the temp one isn't
779 // fully written and delete it.
780 if (real.exists()) {
781 readStateFromFileLocked(real);
782 if (temp.exists()) {
783 temp.delete();
784 }
785 } else if (temp.exists()) {
786 readStateFromFileLocked(temp);
787 temp.renameTo(real);
788 }
789 }
790
791 void saveStateLocked() {
792 File temp = savedStateTempFile();
793 File real = savedStateRealFile();
794
795 if (!real.exists()) {
796 // If the real one doesn't exist, it's either because this is the first time
797 // or because something went wrong while copying them. In this case, we can't
798 // trust anything that's in temp. In order to have the loadState code not
799 // use the temporary one until it's fully written, create an empty file
800 // for real, which will we'll shortly delete.
801 try {
802 real.createNewFile();
803 } catch (IOException e) {
804 }
805 }
806
807 if (temp.exists()) {
808 temp.delete();
809 }
810
811 writeStateToFileLocked(temp);
812
813 real.delete();
814 temp.renameTo(real);
815 }
816
817 void writeStateToFileLocked(File file) {
818 FileOutputStream stream = null;
819 int N;
820
821 try {
822 stream = new FileOutputStream(file, false);
823 XmlSerializer out = new FastXmlSerializer();
824 out.setOutput(stream, "utf-8");
825 out.startDocument(null, true);
826
827
828 out.startTag(null, "gs");
829
830 int providerIndex = 0;
831 N = mInstalledProviders.size();
832 for (int i=0; i<N; i++) {
833 Provider p = mInstalledProviders.get(i);
834 if (p.instances.size() > 0) {
835 out.startTag(null, "p");
836 out.attribute(null, "pkg", p.info.provider.getPackageName());
837 out.attribute(null, "cl", p.info.provider.getClassName());
838 out.endTag(null, "h");
839 p.tag = providerIndex;
840 providerIndex++;
841 }
842 }
843
844 N = mHosts.size();
845 for (int i=0; i<N; i++) {
846 Host host = mHosts.get(i);
847 out.startTag(null, "h");
848 out.attribute(null, "pkg", host.packageName);
849 out.attribute(null, "id", Integer.toHexString(host.hostId));
850 out.endTag(null, "h");
851 host.tag = i;
852 }
853
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700854 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700856 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700858 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 out.attribute(null, "h", Integer.toHexString(id.host.tag));
860 if (id.provider != null) {
861 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
862 }
863 out.endTag(null, "g");
864 }
865
866 out.endTag(null, "gs");
867
868 out.endDocument();
869 stream.close();
870 } catch (IOException e) {
871 try {
872 if (stream != null) {
873 stream.close();
874 }
875 } catch (IOException ex) {
876 }
877 if (file.exists()) {
878 file.delete();
879 }
880 }
881 }
882
883 void readStateFromFileLocked(File file) {
884 FileInputStream stream = null;
885
886 boolean success = false;
887
888 try {
889 stream = new FileInputStream(file);
890 XmlPullParser parser = Xml.newPullParser();
891 parser.setInput(stream, null);
892
893 int type;
894 int providerIndex = 0;
895 HashMap<Integer,Provider> loadedProviders = new HashMap();
896 do {
897 type = parser.next();
898 if (type == XmlPullParser.START_TAG) {
899 String tag = parser.getName();
900 if ("p".equals(tag)) {
901 // TODO: do we need to check that this package has the same signature
902 // as before?
903 String pkg = parser.getAttributeValue(null, "pkg");
904 String cl = parser.getAttributeValue(null, "cl");
905 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
906 if (p == null && mSafeMode) {
907 // if we're in safe mode, make a temporary one
908 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700909 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 p.info.provider = new ComponentName(pkg, cl);
911 p.zombie = true;
912 mInstalledProviders.add(p);
913 }
914 if (p != null) {
915 // if it wasn't uninstalled or something
916 loadedProviders.put(providerIndex, p);
917 }
918 providerIndex++;
919 }
920 else if ("h".equals(tag)) {
921 Host host = new Host();
922
923 // TODO: do we need to check that this package has the same signature
924 // as before?
925 host.packageName = parser.getAttributeValue(null, "pkg");
926 try {
927 host.uid = getUidForPackage(host.packageName);
928 } catch (PackageManager.NameNotFoundException ex) {
929 host.zombie = true;
930 }
931 if (!host.zombie || mSafeMode) {
932 // In safe mode, we don't discard the hosts we don't recognize
933 // so that they're not pruned from our list. Otherwise, we do.
934 host.hostId = Integer.parseInt(
935 parser.getAttributeValue(null, "id"), 16);
936 mHosts.add(host);
937 }
938 }
939 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700940 AppWidgetId id = new AppWidgetId();
941 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
942 if (id.appWidgetId >= mNextAppWidgetId) {
943 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 }
945
946 String providerString = parser.getAttributeValue(null, "p");
947 if (providerString != null) {
948 // there's no provider if it hasn't been bound yet.
949 // maybe we don't have to save this, but it brings the system
950 // to the state it was in.
951 int pIndex = Integer.parseInt(providerString, 16);
952 id.provider = loadedProviders.get(pIndex);
953 if (false) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700954 Log.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 + pIndex + " which is " + id.provider);
956 }
957 if (id.provider == null) {
958 // This provider is gone. We just let the host figure out
959 // that this happened when it fails to load it.
960 continue;
961 }
962 }
963
964 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
965 id.host = mHosts.get(hIndex);
966 if (id.host == null) {
967 // This host is gone.
968 continue;
969 }
970
971 if (id.provider != null) {
972 id.provider.instances.add(id);
973 }
974 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700975 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 }
977 }
978 } while (type != XmlPullParser.END_DOCUMENT);
979 success = true;
980 } catch (NullPointerException e) {
981 Log.w(TAG, "failed parsing " + file, e);
982 } catch (NumberFormatException e) {
983 Log.w(TAG, "failed parsing " + file, e);
984 } catch (XmlPullParserException e) {
985 Log.w(TAG, "failed parsing " + file, e);
986 } catch (IOException e) {
987 Log.w(TAG, "failed parsing " + file, e);
988 } catch (IndexOutOfBoundsException e) {
989 Log.w(TAG, "failed parsing " + file, e);
990 }
991 try {
992 if (stream != null) {
993 stream.close();
994 }
995 } catch (IOException e) {
996 }
997
998 if (success) {
999 // delete any hosts that didn't manage to get connected (should happen)
1000 // if it matters, they'll be reconnected.
1001 final int N = mHosts.size();
1002 for (int i=0; i<N; i++) {
1003 pruneHostLocked(mHosts.get(i));
1004 }
1005 } else {
1006 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001007 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 mHosts.clear();
1009 final int N = mInstalledProviders.size();
1010 for (int i=0; i<N; i++) {
1011 mInstalledProviders.get(i).instances.clear();
1012 }
1013 }
1014 }
1015
1016 File savedStateTempFile() {
1017 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1018 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1019 }
1020
1021 File savedStateRealFile() {
1022 return new File("/data/system/" + SETTINGS_FILENAME);
1023 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1024 }
1025
1026 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1027 public void onReceive(Context context, Intent intent) {
1028 String action = intent.getAction();
1029 //Log.d(TAG, "received " + action);
1030 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1031 sendInitialBroadcasts();
1032 } else {
1033 Uri uri = intent.getData();
1034 if (uri == null) {
1035 return;
1036 }
1037 String pkgName = uri.getSchemeSpecificPart();
1038 if (pkgName == null) {
1039 return;
1040 }
1041
1042 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001043 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 Bundle extras = intent.getExtras();
1045 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1046 // The package was just upgraded
1047 updateProvidersForPackageLocked(pkgName);
1048 } else {
1049 // The package was just added
1050 addProvidersForPackageLocked(pkgName);
1051 }
1052 saveStateLocked();
1053 }
1054 }
1055 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
1056 Bundle extras = intent.getExtras();
1057 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1058 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1059 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001060 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 removeProvidersForPackageLocked(pkgName);
1062 saveStateLocked();
1063 }
1064 }
1065 }
1066 }
1067 }
1068 };
1069
1070 // TODO: If there's a better way of matching an intent filter against the
1071 // packages for a given package, use that.
1072 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001073 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1075 PackageManager.GET_META_DATA);
1076
1077 final int N = broadcastReceivers.size();
1078 for (int i=0; i<N; i++) {
1079 ResolveInfo ri = broadcastReceivers.get(i);
1080 ActivityInfo ai = ri.activityInfo;
1081
1082 if (pkgName.equals(ai.packageName)) {
1083 addProviderLocked(ri);
1084 }
1085 }
1086 }
1087
1088 // TODO: If there's a better way of matching an intent filter against the
1089 // packages for a given package, use that.
1090 void updateProvidersForPackageLocked(String pkgName) {
1091 HashSet<String> keep = new HashSet();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001092 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1094 PackageManager.GET_META_DATA);
1095
1096 // add the missing ones and collect which ones to keep
1097 int N = broadcastReceivers.size();
1098 for (int i=0; i<N; i++) {
1099 ResolveInfo ri = broadcastReceivers.get(i);
1100 ActivityInfo ai = ri.activityInfo;
1101 if (pkgName.equals(ai.packageName)) {
1102 ComponentName component = new ComponentName(ai.packageName, ai.name);
1103 Provider p = lookupProviderLocked(component);
1104 if (p == null) {
1105 if (addProviderLocked(ri)) {
1106 keep.add(ai.name);
1107 }
1108 } else {
1109 Provider parsed = parseProviderInfoXml(component, ri);
1110 if (parsed != null) {
1111 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001112 // Use the new AppWidgetProviderInfo.
1113 AppWidgetProviderInfo oldInfo = p.info;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 p.info = parsed.info;
1115 // If it's enabled
1116 final int M = p.instances.size();
1117 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001118 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 // Reschedule for the new updatePeriodMillis (don't worry about handling
1120 // it specially if updatePeriodMillis didn't change because we just sent
1121 // an update, and the next one will be updatePeriodMillis from now).
1122 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001123 registerForBroadcastsLocked(p, appWidgetIds);
1124 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001126 AppWidgetId id = p.instances.get(j);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 if (id.host != null && id.host.callbacks != null) {
1128 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001129 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 } catch (RemoteException ex) {
1131 // It failed; remove the callback. No need to prune because
1132 // we know that this host is still referenced by this
1133 // instance.
1134 id.host.callbacks = null;
1135 }
1136 }
1137 }
1138 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001139 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 }
1141 }
1142 }
1143 }
1144 }
1145
1146 // prune the ones we don't want to keep
1147 N = mInstalledProviders.size();
1148 for (int i=N-1; i>=0; i--) {
1149 Provider p = mInstalledProviders.get(i);
1150 if (pkgName.equals(p.info.provider.getPackageName())
1151 && !keep.contains(p.info.provider.getClassName())) {
1152 removeProviderLocked(i, p);
1153 }
1154 }
1155 }
1156
1157 void removeProvidersForPackageLocked(String pkgName) {
1158 int N = mInstalledProviders.size();
1159 for (int i=N-1; i>=0; i--) {
1160 Provider p = mInstalledProviders.get(i);
1161 if (pkgName.equals(p.info.provider.getPackageName())) {
1162 removeProviderLocked(i, p);
1163 }
1164 }
1165
1166 // Delete the hosts for this package too
1167 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001168 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 // so we don't need to worry about sending DISABLE broadcasts to them.
1170 N = mHosts.size();
1171 for (int i=N-1; i>=0; i--) {
1172 Host host = mHosts.get(i);
1173 if (pkgName.equals(host.packageName)) {
1174 deleteHostLocked(host);
1175 }
1176 }
1177 }
1178}
1179