blob: bb420a6e8b69f76467603cd0ed0e262ec014d91d [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;
38import android.os.RemoteException;
39import android.os.SystemClock;
40import android.util.AttributeSet;
41import android.util.Log;
42import android.util.Xml;
43import android.widget.RemoteViews;
44
45import java.io.IOException;
46import java.io.File;
47import java.io.FileDescriptor;
48import java.io.FileInputStream;
49import java.io.FileOutputStream;
50import java.io.PrintWriter;
51import java.util.ArrayList;
52import java.util.List;
53import java.util.HashMap;
54import java.util.HashSet;
55
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070056import com.android.internal.appwidget.IAppWidgetService;
57import com.android.internal.appwidget.IAppWidgetHost;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import com.android.internal.util.XmlUtils;
59import com.android.internal.util.FastXmlSerializer;
60
61import org.xmlpull.v1.XmlPullParser;
62import org.xmlpull.v1.XmlPullParserException;
63import org.xmlpull.v1.XmlSerializer;
64
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070065class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070067 private static final String TAG = "AppWidgetService";
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 SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
71
72 /*
73 * When identifying a Host or Provider based on the calling process, use the uid field.
74 * When identifying a Host or Provider based on a package manager broadcast, use the
75 * package given.
76 */
77
78 static class Provider {
79 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070080 AppWidgetProviderInfo info;
81 ArrayList<AppWidgetId> instances = new ArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 PendingIntent broadcast;
83 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
84
85 int tag; // for use while saving state (the index)
86 }
87
88 static class Host {
89 int uid;
90 int hostId;
91 String packageName;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070092 ArrayList<AppWidgetId> instances = new ArrayList();
93 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
95
96 int tag; // for use while saving state (the index)
97 }
98
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070099 static class AppWidgetId {
100 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 Provider provider;
102 RemoteViews views;
103 Host host;
104 }
105
106 Context mContext;
107 PackageManager mPackageManager;
108 AlarmManager mAlarmManager;
109 ArrayList<Provider> mInstalledProviders = new ArrayList();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700110 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
111 ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 ArrayList<Host> mHosts = new ArrayList();
113 boolean mSafeMode;
114
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700115 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 mContext = context;
117 mPackageManager = context.getPackageManager();
118 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
119 }
120
121 public void systemReady(boolean safeMode) {
122 mSafeMode = safeMode;
123
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700124 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 loadStateLocked();
126
127 // Register for the boot completed broadcast, so we can send the
128 // ENABLE broacasts. If we try to send them now, they time out,
129 // because the system isn't ready to handle them yet.
130 mContext.registerReceiver(mBroadcastReceiver,
131 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
132
133 // Register for broadcasts about package install, etc., so we can
134 // update the provider list.
135 IntentFilter filter = new IntentFilter();
136 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
137 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
138 filter.addDataScheme("package");
139 mContext.registerReceiver(mBroadcastReceiver, filter);
140 }
141
142 @Override
143 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
144 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
145 != PackageManager.PERMISSION_GRANTED) {
146 pw.println("Permission Denial: can't dump from from pid="
147 + Binder.getCallingPid()
148 + ", uid=" + Binder.getCallingUid());
149 return;
150 }
151
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700152 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700154 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 for (int i=0; i<N; i++) {
156 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700157 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700158 pw.print(" ["); pw.print(i); pw.print("] provider ");
159 pw.print(info.provider.flattenToShortString());
160 pw.println(':');
161 pw.print(" min=("); pw.print(info.minWidth);
162 pw.print("x"); pw.print(info.minHeight);
163 pw.print(") updatePeriodMillis=");
164 pw.print(info.updatePeriodMillis);
165 pw.print(" initialLayout=#");
166 pw.print(Integer.toHexString(info.initialLayout));
167 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 }
169
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700170 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700171 pw.println(" ");
172 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700174 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700175 pw.print(" ["); pw.print(i); pw.print("] id=");
176 pw.println(id.appWidgetId);;
177 pw.print(" hostId=");
178 pw.print(id.host.hostId); pw.print(' ');
179 pw.print(id.host.packageName); pw.print('/');
180 pw.println(id.host.uid);
181 if (id.provider != null) {
182 pw.print(" provider=");
183 pw.println(id.provider.info.provider.flattenToShortString());
184 }
185 if (id.host != null) {
186 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
187 }
188 if (id.views != null) {
189 pw.print(" views="); pw.println(id.views);
190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 }
192
193 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700194 pw.println(" ");
195 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 for (int i=0; i<N; i++) {
197 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700198 pw.print(" ["); pw.print(i); pw.print("] hostId=");
199 pw.print(host.hostId); pw.print(' ');
200 pw.print(host.packageName); pw.print('/');
201 pw.print(host.uid); pw.println(':');
202 pw.print(" callbacks="); pw.println(host.callbacks);
203 pw.print(" instances.size="); pw.print(host.instances.size());
204 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 }
206 }
207 }
208
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700209 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700211 synchronized (mAppWidgetIds) {
212 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213
214 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
215
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700216 AppWidgetId id = new AppWidgetId();
217 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 id.host = host;
219
220 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700221 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222
223 saveStateLocked();
224
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700225 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 }
227 }
228
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700229 public void deleteAppWidgetId(int appWidgetId) {
230 synchronized (mAppWidgetIds) {
231 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700233 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 saveStateLocked();
235 }
236 }
237 }
238
239 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700240 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 int callingUid = getCallingUid();
242 Host host = lookupHostLocked(callingUid, hostId);
243 if (host != null) {
244 deleteHostLocked(host);
245 saveStateLocked();
246 }
247 }
248 }
249
250 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700251 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 int callingUid = getCallingUid();
253 final int N = mHosts.size();
254 boolean changed = false;
255 for (int i=N-1; i>=0; i--) {
256 Host host = mHosts.get(i);
257 if (host.uid == callingUid) {
258 deleteHostLocked(host);
259 changed = true;
260 }
261 }
262 if (changed) {
263 saveStateLocked();
264 }
265 }
266 }
267
268 void deleteHostLocked(Host host) {
269 final int N = host.instances.size();
270 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700271 AppWidgetId id = host.instances.get(i);
272 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 }
274 host.instances.clear();
275 mHosts.remove(host);
276 // it's gone or going away, abruptly drop the callback connection
277 host.callbacks = null;
278 }
279
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700280 void deleteAppWidgetLocked(AppWidgetId id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 Host host = id.host;
282 host.instances.remove(id);
283 pruneHostLocked(host);
284
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700285 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286
287 Provider p = id.provider;
288 if (p != null) {
289 p.instances.remove(id);
290 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700291 // send the broacast saying that this appWidgetId has been deleted
292 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700294 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 mContext.sendBroadcast(intent);
296 if (p.instances.size() == 0) {
297 // cancel the future updates
298 cancelBroadcasts(p);
299
300 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700301 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 intent.setComponent(p.info.provider);
303 mContext.sendBroadcast(intent);
304 }
305 }
306 }
307 }
308
309 void cancelBroadcasts(Provider p) {
310 if (p.broadcast != null) {
311 mAlarmManager.cancel(p.broadcast);
312 long token = Binder.clearCallingIdentity();
313 try {
314 p.broadcast.cancel();
315 } finally {
316 Binder.restoreCallingIdentity(token);
317 }
318 p.broadcast = null;
319 }
320 }
321
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700322 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
323 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
324 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
325 synchronized (mAppWidgetIds) {
326 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700328 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 }
330 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700331 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 + id.provider.info.provider);
333 }
334 Provider p = lookupProviderLocked(provider);
335 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700336 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 }
338 if (p.zombie) {
339 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
340 + " safe mode: " + provider);
341 }
342
343 id.provider = p;
344 p.instances.add(id);
345 int instancesSize = p.instances.size();
346 if (instancesSize == 1) {
347 // tell the provider that it's ready
348 sendEnableIntentLocked(p);
349 }
350
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700351 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 // It's less critical when the next one happens, so when we schdule the next one,
353 // we add updatePeriodMillis to its start time. That time will have some slop,
354 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700355 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356
357 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700358 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 saveStateLocked();
360 }
361 }
362
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700363 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
364 synchronized (mAppWidgetIds) {
365 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 if (id != null && id.provider != null && !id.provider.zombie) {
367 return id.provider.info;
368 }
369 return null;
370 }
371 }
372
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700373 public RemoteViews getAppWidgetViews(int appWidgetId) {
374 synchronized (mAppWidgetIds) {
375 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 if (id != null) {
377 return id.views;
378 }
379 return null;
380 }
381 }
382
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700383 public List<AppWidgetProviderInfo> getInstalledProviders() {
384 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 final int N = mInstalledProviders.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700386 ArrayList<AppWidgetProviderInfo> result = new ArrayList(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 for (int i=0; i<N; i++) {
388 Provider p = mInstalledProviders.get(i);
389 if (!p.zombie) {
390 result.add(p.info);
391 }
392 }
393 return result;
394 }
395 }
396
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700397 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
398 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 return;
400 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700401 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 return;
403 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700404 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700406 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700408 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
409 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 }
411 }
412 }
413
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700414 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
415 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 Provider p = lookupProviderLocked(provider);
417 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700418 Log.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 return;
420 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700421 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 final int N = instances.size();
423 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700424 AppWidgetId id = instances.get(i);
425 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 }
427 }
428 }
429
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700430 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
431 // allow for stale appWidgetIds and other badness
432 // lookup also checks that the calling process can access the appWidgetId
433 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
435 id.views = views;
436
437 // is anyone listening?
438 if (id.host.callbacks != null) {
439 try {
440 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700441 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 } catch (RemoteException e) {
443 // It failed; remove the callback. No need to prune because
444 // we know that this host is still referenced by this instance.
445 id.host.callbacks = null;
446 }
447 }
448 }
449 }
450
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700451 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 List<RemoteViews> updatedViews) {
453 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700454 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
456 host.callbacks = callbacks;
457
458 updatedViews.clear();
459
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700460 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 int N = instances.size();
462 int[] updatedIds = new int[N];
463 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700464 AppWidgetId id = instances.get(i);
465 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 updatedViews.add(id.views);
467 }
468 return updatedIds;
469 }
470 }
471
472 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700473 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 Host host = lookupHostLocked(getCallingUid(), hostId);
475 host.callbacks = null;
476 pruneHostLocked(host);
477 }
478 }
479
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700480 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700482 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 return true;
484 }
485 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700486 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 return true;
488 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700489 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700491 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 return true;
493 }
494 // Nobody else can access it.
495 return false;
496 }
497
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700498 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700500 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700502 AppWidgetId id = mAppWidgetIds.get(i);
503 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 return id;
505 }
506 }
507 return null;
508 }
509
510 Provider lookupProviderLocked(ComponentName provider) {
511 final int N = mInstalledProviders.size();
512 for (int i=0; i<N; i++) {
513 Provider p = mInstalledProviders.get(i);
514 if (p.info.provider.equals(provider)) {
515 return p;
516 }
517 }
518 return null;
519 }
520
521 Host lookupHostLocked(int uid, int hostId) {
522 final int N = mHosts.size();
523 for (int i=0; i<N; i++) {
524 Host h = mHosts.get(i);
525 if (h.uid == uid && h.hostId == hostId) {
526 return h;
527 }
528 }
529 return null;
530 }
531
532 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
533 final int N = mHosts.size();
534 for (int i=0; i<N; i++) {
535 Host h = mHosts.get(i);
536 if (h.hostId == hostId && h.packageName.equals(packageName)) {
537 return h;
538 }
539 }
540 Host host = new Host();
541 host.packageName = packageName;
542 host.uid = uid;
543 host.hostId = hostId;
544 mHosts.add(host);
545 return host;
546 }
547
548 void pruneHostLocked(Host host) {
549 if (host.instances.size() == 0 && host.callbacks == null) {
550 mHosts.remove(host);
551 }
552 }
553
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700554 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 PackageManager pm = mPackageManager;
556
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700557 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
559 PackageManager.GET_META_DATA);
560
561 final int N = broadcastReceivers.size();
562 for (int i=0; i<N; i++) {
563 ResolveInfo ri = broadcastReceivers.get(i);
564 addProviderLocked(ri);
565 }
566 }
567
568 boolean addProviderLocked(ResolveInfo ri) {
569 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
570 ri.activityInfo.name), ri);
571 if (p != null) {
572 mInstalledProviders.add(p);
573 return true;
574 } else {
575 return false;
576 }
577 }
578
579 void removeProviderLocked(int index, Provider p) {
580 int N = p.instances.size();
581 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700582 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700584 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 // Stop telling the host about updates for this from now on
586 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700587 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700589 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 id.provider = null;
591 pruneHostLocked(id.host);
592 id.host = null;
593 }
594 p.instances.clear();
595 mInstalledProviders.remove(index);
596 // no need to send the DISABLE broadcast, since the receiver is gone anyway
597 cancelBroadcasts(p);
598 }
599
600 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700601 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 intent.setComponent(p.info.provider);
603 mContext.sendBroadcast(intent);
604 }
605
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700606 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
607 if (appWidgetIds != null && appWidgetIds.length > 0) {
608 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
609 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 intent.setComponent(p.info.provider);
611 mContext.sendBroadcast(intent);
612 }
613 }
614
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700615 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 if (p.info.updatePeriodMillis > 0) {
617 // if this is the first instance, set the alarm. otherwise,
618 // rely on the fact that we've already set it and that
619 // PendingIntent.getBroadcast will update the extras.
620 boolean alreadyRegistered = p.broadcast != null;
621 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700622 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
623 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 intent.setComponent(p.info.provider);
625 long token = Binder.clearCallingIdentity();
626 try {
627 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
628 PendingIntent.FLAG_UPDATE_CURRENT);
629 } finally {
630 Binder.restoreCallingIdentity(token);
631 }
632 if (!alreadyRegistered) {
633 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
634 SystemClock.elapsedRealtime() + p.info.updatePeriodMillis,
635 p.info.updatePeriodMillis, p.broadcast);
636 }
637 }
638 }
639
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700640 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700642 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700644 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700646 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 }
648
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700649 public int[] getAppWidgetIds(ComponentName provider) {
650 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 Provider p = lookupProviderLocked(provider);
652 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700653 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 } else {
655 return new int[0];
656 }
657 }
658 }
659
660 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
661 Provider p = null;
662
663 ActivityInfo activityInfo = ri.activityInfo;
664 XmlResourceParser parser = null;
665 try {
666 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700667 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 if (parser == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700669 Log.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
670 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 return null;
672 }
673
674 AttributeSet attrs = Xml.asAttributeSet(parser);
675
676 int type;
677 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
678 && type != XmlPullParser.START_TAG) {
679 // drain whitespace, comments, etc.
680 }
681
682 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700683 if (!"appwidget-provider".equals(nodeName)) {
684 Log.w(TAG, "Meta-data does not start with appwidget-provider tag for"
685 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 return null;
687 }
688
689 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700690 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691
692 info.provider = component;
693 p.uid = activityInfo.applicationInfo.uid;
694
695 TypedArray sa = mContext.getResources().obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700696 com.android.internal.R.styleable.AppWidgetProviderInfo);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 info.minWidth = sa.getDimensionPixelSize(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700698 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 info.minHeight = sa.getDimensionPixelSize(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700700 com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700702 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700704 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700706 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 if (className != null) {
708 info.configure = new ComponentName(component.getPackageName(), className);
709 }
710 info.label = activityInfo.loadLabel(mPackageManager).toString();
711 info.icon = ri.getIconResource();
712 sa.recycle();
713 } catch (Exception e) {
714 // Ok to catch Exception here, because anything going wrong because
715 // of what a client process passes to us should not be fatal for the
716 // system process.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700717 Log.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 return null;
719 } finally {
720 if (parser != null) parser.close();
721 }
722 return p;
723 }
724
725 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
726 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
727 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
728 throw new PackageManager.NameNotFoundException();
729 }
730 return pkgInfo.applicationInfo.uid;
731 }
732
733 int enforceCallingUid(String packageName) throws IllegalArgumentException {
734 int callingUid = getCallingUid();
735 int packageUid;
736 try {
737 packageUid = getUidForPackage(packageName);
738 } catch (PackageManager.NameNotFoundException ex) {
739 throw new IllegalArgumentException("packageName and uid don't match packageName="
740 + packageName);
741 }
742 if (callingUid != packageUid) {
743 throw new IllegalArgumentException("packageName and uid don't match packageName="
744 + packageName);
745 }
746 return callingUid;
747 }
748
749 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700750 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 final int N = mInstalledProviders.size();
752 for (int i=0; i<N; i++) {
753 Provider p = mInstalledProviders.get(i);
754 if (p.instances.size() > 0) {
755 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700756 int[] appWidgetIds = getAppWidgetIds(p);
757 sendUpdateIntentLocked(p, appWidgetIds);
758 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 }
760 }
761 }
762 }
763
764 // only call from initialization -- it assumes that the data structures are all empty
765 void loadStateLocked() {
766 File temp = savedStateTempFile();
767 File real = savedStateRealFile();
768
769 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
770 // real one. if there is both a real file and a temp one, assume that the temp one isn't
771 // fully written and delete it.
772 if (real.exists()) {
773 readStateFromFileLocked(real);
774 if (temp.exists()) {
775 temp.delete();
776 }
777 } else if (temp.exists()) {
778 readStateFromFileLocked(temp);
779 temp.renameTo(real);
780 }
781 }
782
783 void saveStateLocked() {
784 File temp = savedStateTempFile();
785 File real = savedStateRealFile();
786
787 if (!real.exists()) {
788 // If the real one doesn't exist, it's either because this is the first time
789 // or because something went wrong while copying them. In this case, we can't
790 // trust anything that's in temp. In order to have the loadState code not
791 // use the temporary one until it's fully written, create an empty file
792 // for real, which will we'll shortly delete.
793 try {
794 real.createNewFile();
795 } catch (IOException e) {
796 }
797 }
798
799 if (temp.exists()) {
800 temp.delete();
801 }
802
803 writeStateToFileLocked(temp);
804
805 real.delete();
806 temp.renameTo(real);
807 }
808
809 void writeStateToFileLocked(File file) {
810 FileOutputStream stream = null;
811 int N;
812
813 try {
814 stream = new FileOutputStream(file, false);
815 XmlSerializer out = new FastXmlSerializer();
816 out.setOutput(stream, "utf-8");
817 out.startDocument(null, true);
818
819
820 out.startTag(null, "gs");
821
822 int providerIndex = 0;
823 N = mInstalledProviders.size();
824 for (int i=0; i<N; i++) {
825 Provider p = mInstalledProviders.get(i);
826 if (p.instances.size() > 0) {
827 out.startTag(null, "p");
828 out.attribute(null, "pkg", p.info.provider.getPackageName());
829 out.attribute(null, "cl", p.info.provider.getClassName());
830 out.endTag(null, "h");
831 p.tag = providerIndex;
832 providerIndex++;
833 }
834 }
835
836 N = mHosts.size();
837 for (int i=0; i<N; i++) {
838 Host host = mHosts.get(i);
839 out.startTag(null, "h");
840 out.attribute(null, "pkg", host.packageName);
841 out.attribute(null, "id", Integer.toHexString(host.hostId));
842 out.endTag(null, "h");
843 host.tag = i;
844 }
845
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700846 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700848 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700850 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 out.attribute(null, "h", Integer.toHexString(id.host.tag));
852 if (id.provider != null) {
853 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
854 }
855 out.endTag(null, "g");
856 }
857
858 out.endTag(null, "gs");
859
860 out.endDocument();
861 stream.close();
862 } catch (IOException e) {
863 try {
864 if (stream != null) {
865 stream.close();
866 }
867 } catch (IOException ex) {
868 }
869 if (file.exists()) {
870 file.delete();
871 }
872 }
873 }
874
875 void readStateFromFileLocked(File file) {
876 FileInputStream stream = null;
877
878 boolean success = false;
879
880 try {
881 stream = new FileInputStream(file);
882 XmlPullParser parser = Xml.newPullParser();
883 parser.setInput(stream, null);
884
885 int type;
886 int providerIndex = 0;
887 HashMap<Integer,Provider> loadedProviders = new HashMap();
888 do {
889 type = parser.next();
890 if (type == XmlPullParser.START_TAG) {
891 String tag = parser.getName();
892 if ("p".equals(tag)) {
893 // TODO: do we need to check that this package has the same signature
894 // as before?
895 String pkg = parser.getAttributeValue(null, "pkg");
896 String cl = parser.getAttributeValue(null, "cl");
897 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
898 if (p == null && mSafeMode) {
899 // if we're in safe mode, make a temporary one
900 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700901 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 p.info.provider = new ComponentName(pkg, cl);
903 p.zombie = true;
904 mInstalledProviders.add(p);
905 }
906 if (p != null) {
907 // if it wasn't uninstalled or something
908 loadedProviders.put(providerIndex, p);
909 }
910 providerIndex++;
911 }
912 else if ("h".equals(tag)) {
913 Host host = new Host();
914
915 // TODO: do we need to check that this package has the same signature
916 // as before?
917 host.packageName = parser.getAttributeValue(null, "pkg");
918 try {
919 host.uid = getUidForPackage(host.packageName);
920 } catch (PackageManager.NameNotFoundException ex) {
921 host.zombie = true;
922 }
923 if (!host.zombie || mSafeMode) {
924 // In safe mode, we don't discard the hosts we don't recognize
925 // so that they're not pruned from our list. Otherwise, we do.
926 host.hostId = Integer.parseInt(
927 parser.getAttributeValue(null, "id"), 16);
928 mHosts.add(host);
929 }
930 }
931 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700932 AppWidgetId id = new AppWidgetId();
933 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
934 if (id.appWidgetId >= mNextAppWidgetId) {
935 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 }
937
938 String providerString = parser.getAttributeValue(null, "p");
939 if (providerString != null) {
940 // there's no provider if it hasn't been bound yet.
941 // maybe we don't have to save this, but it brings the system
942 // to the state it was in.
943 int pIndex = Integer.parseInt(providerString, 16);
944 id.provider = loadedProviders.get(pIndex);
945 if (false) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700946 Log.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 + pIndex + " which is " + id.provider);
948 }
949 if (id.provider == null) {
950 // This provider is gone. We just let the host figure out
951 // that this happened when it fails to load it.
952 continue;
953 }
954 }
955
956 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
957 id.host = mHosts.get(hIndex);
958 if (id.host == null) {
959 // This host is gone.
960 continue;
961 }
962
963 if (id.provider != null) {
964 id.provider.instances.add(id);
965 }
966 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700967 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 }
969 }
970 } while (type != XmlPullParser.END_DOCUMENT);
971 success = true;
972 } catch (NullPointerException e) {
973 Log.w(TAG, "failed parsing " + file, e);
974 } catch (NumberFormatException e) {
975 Log.w(TAG, "failed parsing " + file, e);
976 } catch (XmlPullParserException e) {
977 Log.w(TAG, "failed parsing " + file, e);
978 } catch (IOException e) {
979 Log.w(TAG, "failed parsing " + file, e);
980 } catch (IndexOutOfBoundsException e) {
981 Log.w(TAG, "failed parsing " + file, e);
982 }
983 try {
984 if (stream != null) {
985 stream.close();
986 }
987 } catch (IOException e) {
988 }
989
990 if (success) {
991 // delete any hosts that didn't manage to get connected (should happen)
992 // if it matters, they'll be reconnected.
993 final int N = mHosts.size();
994 for (int i=0; i<N; i++) {
995 pruneHostLocked(mHosts.get(i));
996 }
997 } else {
998 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700999 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 mHosts.clear();
1001 final int N = mInstalledProviders.size();
1002 for (int i=0; i<N; i++) {
1003 mInstalledProviders.get(i).instances.clear();
1004 }
1005 }
1006 }
1007
1008 File savedStateTempFile() {
1009 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1010 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1011 }
1012
1013 File savedStateRealFile() {
1014 return new File("/data/system/" + SETTINGS_FILENAME);
1015 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1016 }
1017
1018 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1019 public void onReceive(Context context, Intent intent) {
1020 String action = intent.getAction();
1021 //Log.d(TAG, "received " + action);
1022 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1023 sendInitialBroadcasts();
1024 } else {
1025 Uri uri = intent.getData();
1026 if (uri == null) {
1027 return;
1028 }
1029 String pkgName = uri.getSchemeSpecificPart();
1030 if (pkgName == null) {
1031 return;
1032 }
1033
1034 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001035 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 Bundle extras = intent.getExtras();
1037 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1038 // The package was just upgraded
1039 updateProvidersForPackageLocked(pkgName);
1040 } else {
1041 // The package was just added
1042 addProvidersForPackageLocked(pkgName);
1043 }
1044 saveStateLocked();
1045 }
1046 }
1047 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
1048 Bundle extras = intent.getExtras();
1049 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1050 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1051 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001052 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 removeProvidersForPackageLocked(pkgName);
1054 saveStateLocked();
1055 }
1056 }
1057 }
1058 }
1059 }
1060 };
1061
1062 // TODO: If there's a better way of matching an intent filter against the
1063 // packages for a given package, use that.
1064 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001065 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1067 PackageManager.GET_META_DATA);
1068
1069 final int N = broadcastReceivers.size();
1070 for (int i=0; i<N; i++) {
1071 ResolveInfo ri = broadcastReceivers.get(i);
1072 ActivityInfo ai = ri.activityInfo;
1073
1074 if (pkgName.equals(ai.packageName)) {
1075 addProviderLocked(ri);
1076 }
1077 }
1078 }
1079
1080 // TODO: If there's a better way of matching an intent filter against the
1081 // packages for a given package, use that.
1082 void updateProvidersForPackageLocked(String pkgName) {
1083 HashSet<String> keep = new HashSet();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001084 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1086 PackageManager.GET_META_DATA);
1087
1088 // add the missing ones and collect which ones to keep
1089 int N = broadcastReceivers.size();
1090 for (int i=0; i<N; i++) {
1091 ResolveInfo ri = broadcastReceivers.get(i);
1092 ActivityInfo ai = ri.activityInfo;
1093 if (pkgName.equals(ai.packageName)) {
1094 ComponentName component = new ComponentName(ai.packageName, ai.name);
1095 Provider p = lookupProviderLocked(component);
1096 if (p == null) {
1097 if (addProviderLocked(ri)) {
1098 keep.add(ai.name);
1099 }
1100 } else {
1101 Provider parsed = parseProviderInfoXml(component, ri);
1102 if (parsed != null) {
1103 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001104 // Use the new AppWidgetProviderInfo.
1105 AppWidgetProviderInfo oldInfo = p.info;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 p.info = parsed.info;
1107 // If it's enabled
1108 final int M = p.instances.size();
1109 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001110 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 // Reschedule for the new updatePeriodMillis (don't worry about handling
1112 // it specially if updatePeriodMillis didn't change because we just sent
1113 // an update, and the next one will be updatePeriodMillis from now).
1114 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001115 registerForBroadcastsLocked(p, appWidgetIds);
1116 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001117 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001118 AppWidgetId id = p.instances.get(j);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 if (id.host != null && id.host.callbacks != null) {
1120 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001121 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 } catch (RemoteException ex) {
1123 // It failed; remove the callback. No need to prune because
1124 // we know that this host is still referenced by this
1125 // instance.
1126 id.host.callbacks = null;
1127 }
1128 }
1129 }
1130 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001131 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 }
1133 }
1134 }
1135 }
1136 }
1137
1138 // prune the ones we don't want to keep
1139 N = mInstalledProviders.size();
1140 for (int i=N-1; i>=0; i--) {
1141 Provider p = mInstalledProviders.get(i);
1142 if (pkgName.equals(p.info.provider.getPackageName())
1143 && !keep.contains(p.info.provider.getClassName())) {
1144 removeProviderLocked(i, p);
1145 }
1146 }
1147 }
1148
1149 void removeProvidersForPackageLocked(String pkgName) {
1150 int N = mInstalledProviders.size();
1151 for (int i=N-1; i>=0; i--) {
1152 Provider p = mInstalledProviders.get(i);
1153 if (pkgName.equals(p.info.provider.getPackageName())) {
1154 removeProviderLocked(i, p);
1155 }
1156 }
1157
1158 // Delete the hosts for this package too
1159 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001160 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161 // so we don't need to worry about sending DISABLE broadcasts to them.
1162 N = mHosts.size();
1163 for (int i=N-1; i>=0; i--) {
1164 Host host = mHosts.get(i);
1165 if (pkgName.equals(host.packageName)) {
1166 deleteHostLocked(host);
1167 }
1168 }
1169 }
1170}
1171