blob: f825df9bce72b8463cd09a1d92e158b63b4b80b4 [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;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080032import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import 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;
Joe Onorato8a9b2202010-02-26 18:56:32 -080042import android.util.Slog;
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;
Eric Fischer63c2d9e2009-10-22 15:22:50 -070055import java.util.Locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import java.util.HashMap;
57import java.util.HashSet;
58
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070059import com.android.internal.appwidget.IAppWidgetService;
60import com.android.internal.appwidget.IAppWidgetHost;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080061import com.android.internal.util.FastXmlSerializer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062
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";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070073 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
75 /*
76 * When identifying a Host or Provider based on the calling process, use the uid field.
77 * When identifying a Host or Provider based on a package manager broadcast, use the
78 * package given.
79 */
80
81 static class Provider {
82 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070083 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070084 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 PendingIntent broadcast;
86 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
87
88 int tag; // for use while saving state (the index)
89 }
90
91 static class Host {
92 int uid;
93 int hostId;
94 String packageName;
Romain Guya5475592009-07-01 17:20:08 -070095 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070096 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
98
99 int tag; // for use while saving state (the index)
100 }
101
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700102 static class AppWidgetId {
103 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 Provider provider;
105 RemoteViews views;
106 Host host;
107 }
108
109 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700110 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 PackageManager mPackageManager;
112 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700113 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700114 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700115 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
116 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 boolean mSafeMode;
118
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700119 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 mContext = context;
121 mPackageManager = context.getPackageManager();
122 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
123 }
124
125 public void systemReady(boolean safeMode) {
126 mSafeMode = safeMode;
127
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700128 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 loadStateLocked();
130
131 // Register for the boot completed broadcast, so we can send the
132 // ENABLE broacasts. If we try to send them now, they time out,
133 // because the system isn't ready to handle them yet.
134 mContext.registerReceiver(mBroadcastReceiver,
135 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
136
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700137 // Register for configuration changes so we can update the names
138 // of the widgets when the locale changes.
139 mContext.registerReceiver(mBroadcastReceiver,
140 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
141
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 // Register for broadcasts about package install, etc., so we can
143 // update the provider list.
144 IntentFilter filter = new IntentFilter();
145 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
146 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
147 filter.addDataScheme("package");
148 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800149 // Register for events related to sdcard installation.
150 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800151 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
152 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800153 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 }
155
156 @Override
157 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
158 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
159 != PackageManager.PERMISSION_GRANTED) {
160 pw.println("Permission Denial: can't dump from from pid="
161 + Binder.getCallingPid()
162 + ", uid=" + Binder.getCallingUid());
163 return;
164 }
165
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700166 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700168 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 for (int i=0; i<N; i++) {
170 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700171 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700172 pw.print(" ["); pw.print(i); pw.print("] provider ");
173 pw.print(info.provider.flattenToShortString());
174 pw.println(':');
175 pw.print(" min=("); pw.print(info.minWidth);
176 pw.print("x"); pw.print(info.minHeight);
177 pw.print(") updatePeriodMillis=");
178 pw.print(info.updatePeriodMillis);
179 pw.print(" initialLayout=#");
180 pw.print(Integer.toHexString(info.initialLayout));
181 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 }
183
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700184 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700185 pw.println(" ");
186 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700188 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700189 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700190 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700191 pw.print(" hostId=");
192 pw.print(id.host.hostId); pw.print(' ');
193 pw.print(id.host.packageName); pw.print('/');
194 pw.println(id.host.uid);
195 if (id.provider != null) {
196 pw.print(" provider=");
197 pw.println(id.provider.info.provider.flattenToShortString());
198 }
199 if (id.host != null) {
200 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
201 }
202 if (id.views != null) {
203 pw.print(" views="); pw.println(id.views);
204 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 }
206
207 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700208 pw.println(" ");
209 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 for (int i=0; i<N; i++) {
211 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700212 pw.print(" ["); pw.print(i); pw.print("] hostId=");
213 pw.print(host.hostId); pw.print(' ');
214 pw.print(host.packageName); pw.print('/');
215 pw.print(host.uid); pw.println(':');
216 pw.print(" callbacks="); pw.println(host.callbacks);
217 pw.print(" instances.size="); pw.print(host.instances.size());
218 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 }
220 }
221 }
222
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700223 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700225 synchronized (mAppWidgetIds) {
226 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227
228 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
229
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700230 AppWidgetId id = new AppWidgetId();
231 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 id.host = host;
233
234 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700235 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236
237 saveStateLocked();
238
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700239 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 }
241 }
242
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700243 public void deleteAppWidgetId(int appWidgetId) {
244 synchronized (mAppWidgetIds) {
245 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700247 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 saveStateLocked();
249 }
250 }
251 }
252
253 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700254 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 int callingUid = getCallingUid();
256 Host host = lookupHostLocked(callingUid, hostId);
257 if (host != null) {
258 deleteHostLocked(host);
259 saveStateLocked();
260 }
261 }
262 }
263
264 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700265 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 int callingUid = getCallingUid();
267 final int N = mHosts.size();
268 boolean changed = false;
269 for (int i=N-1; i>=0; i--) {
270 Host host = mHosts.get(i);
271 if (host.uid == callingUid) {
272 deleteHostLocked(host);
273 changed = true;
274 }
275 }
276 if (changed) {
277 saveStateLocked();
278 }
279 }
280 }
281
282 void deleteHostLocked(Host host) {
283 final int N = host.instances.size();
284 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700285 AppWidgetId id = host.instances.get(i);
286 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
288 host.instances.clear();
289 mHosts.remove(host);
290 // it's gone or going away, abruptly drop the callback connection
291 host.callbacks = null;
292 }
293
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700294 void deleteAppWidgetLocked(AppWidgetId id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 Host host = id.host;
296 host.instances.remove(id);
297 pruneHostLocked(host);
298
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700299 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300
301 Provider p = id.provider;
302 if (p != null) {
303 p.instances.remove(id);
304 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700305 // send the broacast saying that this appWidgetId has been deleted
306 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700308 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 mContext.sendBroadcast(intent);
310 if (p.instances.size() == 0) {
311 // cancel the future updates
312 cancelBroadcasts(p);
313
314 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700315 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 intent.setComponent(p.info.provider);
317 mContext.sendBroadcast(intent);
318 }
319 }
320 }
321 }
322
323 void cancelBroadcasts(Provider p) {
324 if (p.broadcast != null) {
325 mAlarmManager.cancel(p.broadcast);
326 long token = Binder.clearCallingIdentity();
327 try {
328 p.broadcast.cancel();
329 } finally {
330 Binder.restoreCallingIdentity(token);
331 }
332 p.broadcast = null;
333 }
334 }
335
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700336 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
337 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
338 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
339 synchronized (mAppWidgetIds) {
340 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700342 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 }
344 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700345 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 + id.provider.info.provider);
347 }
348 Provider p = lookupProviderLocked(provider);
349 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700350 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
352 if (p.zombie) {
353 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
354 + " safe mode: " + provider);
355 }
356
357 id.provider = p;
358 p.instances.add(id);
359 int instancesSize = p.instances.size();
360 if (instancesSize == 1) {
361 // tell the provider that it's ready
362 sendEnableIntentLocked(p);
363 }
364
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700365 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 // It's less critical when the next one happens, so when we schdule the next one,
367 // we add updatePeriodMillis to its start time. That time will have some slop,
368 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700369 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370
371 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700372 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 saveStateLocked();
374 }
375 }
376
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700377 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
378 synchronized (mAppWidgetIds) {
379 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 if (id != null && id.provider != null && !id.provider.zombie) {
381 return id.provider.info;
382 }
383 return null;
384 }
385 }
386
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700387 public RemoteViews getAppWidgetViews(int appWidgetId) {
388 synchronized (mAppWidgetIds) {
389 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 if (id != null) {
391 return id.views;
392 }
393 return null;
394 }
395 }
396
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700397 public List<AppWidgetProviderInfo> getInstalledProviders() {
398 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700400 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 for (int i=0; i<N; i++) {
402 Provider p = mInstalledProviders.get(i);
403 if (!p.zombie) {
404 result.add(p.info);
405 }
406 }
407 return result;
408 }
409 }
410
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700411 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
412 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 return;
414 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700415 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 return;
417 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700418 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700420 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700422 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
423 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 }
425 }
426 }
427
Adam Cohen2dd21972010-08-15 18:20:04 -0700428 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
429 if (appWidgetIds == null) {
430 return;
431 }
432 if (appWidgetIds.length == 0) {
433 return;
434 }
435 final int N = appWidgetIds.length;
436
437 synchronized (mAppWidgetIds) {
438 for (int i=0; i<N; i++) {
439 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
440 updateAppWidgetInstanceLocked(id, views, true);
441 }
442 }
443 }
444
Winson Chung6394c0e2010-08-16 10:14:56 -0700445 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700446 if (appWidgetIds == null) {
447 return;
448 }
449 if (appWidgetIds.length == 0) {
450 return;
451 }
452 final int N = appWidgetIds.length;
453
454 synchronized (mAppWidgetIds) {
455 for (int i=0; i<N; i++) {
456 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700457 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700458 }
459 }
460 }
461
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700462 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
463 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 Provider p = lookupProviderLocked(provider);
465 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800466 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 return;
468 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700469 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 final int N = instances.size();
471 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700472 AppWidgetId id = instances.get(i);
473 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 }
475 }
476 }
477
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700478 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700479 updateAppWidgetInstanceLocked(id, views, false);
480 }
481
482 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700483 // allow for stale appWidgetIds and other badness
484 // lookup also checks that the calling process can access the appWidgetId
485 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700487
488 // We do not want to save this RemoteViews
489 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490
491 // is anyone listening?
492 if (id.host.callbacks != null) {
493 try {
494 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700495 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 } catch (RemoteException e) {
497 // It failed; remove the callback. No need to prune because
498 // we know that this host is still referenced by this instance.
499 id.host.callbacks = null;
500 }
501 }
502 }
503 }
504
Winson Chung6394c0e2010-08-16 10:14:56 -0700505 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700506 // allow for stale appWidgetIds and other badness
507 // lookup also checks that the calling process can access the appWidgetId
508 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
509 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700510 // is anyone listening?
511 if (id.host.callbacks != null) {
512 try {
513 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700514 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700515 } catch (RemoteException e) {
516 // It failed; remove the callback. No need to prune because
517 // we know that this host is still referenced by this instance.
518 id.host.callbacks = null;
519 }
520 }
521 }
522 }
523
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700524 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 List<RemoteViews> updatedViews) {
526 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700527 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
529 host.callbacks = callbacks;
530
531 updatedViews.clear();
532
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700533 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 int N = instances.size();
535 int[] updatedIds = new int[N];
536 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700537 AppWidgetId id = instances.get(i);
538 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 updatedViews.add(id.views);
540 }
541 return updatedIds;
542 }
543 }
544
545 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700546 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700548 if (host != null) {
549 host.callbacks = null;
550 pruneHostLocked(host);
551 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 }
553 }
554
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700555 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700557 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 return true;
559 }
560 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700561 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 return true;
563 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700564 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700566 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 return true;
568 }
569 // Nobody else can access it.
570 return false;
571 }
572
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700573 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700575 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700577 AppWidgetId id = mAppWidgetIds.get(i);
578 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 return id;
580 }
581 }
582 return null;
583 }
584
585 Provider lookupProviderLocked(ComponentName provider) {
Romain Guyd2671e12010-03-11 18:06:42 -0800586 final String className = provider.getClassName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 final int N = mInstalledProviders.size();
588 for (int i=0; i<N; i++) {
589 Provider p = mInstalledProviders.get(i);
Romain Guyd2671e12010-03-11 18:06:42 -0800590 if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 return p;
592 }
593 }
594 return null;
595 }
596
597 Host lookupHostLocked(int uid, int hostId) {
598 final int N = mHosts.size();
599 for (int i=0; i<N; i++) {
600 Host h = mHosts.get(i);
601 if (h.uid == uid && h.hostId == hostId) {
602 return h;
603 }
604 }
605 return null;
606 }
607
608 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
609 final int N = mHosts.size();
610 for (int i=0; i<N; i++) {
611 Host h = mHosts.get(i);
612 if (h.hostId == hostId && h.packageName.equals(packageName)) {
613 return h;
614 }
615 }
616 Host host = new Host();
617 host.packageName = packageName;
618 host.uid = uid;
619 host.hostId = hostId;
620 mHosts.add(host);
621 return host;
622 }
623
624 void pruneHostLocked(Host host) {
625 if (host.instances.size() == 0 && host.callbacks == null) {
626 mHosts.remove(host);
627 }
628 }
629
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700630 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 PackageManager pm = mPackageManager;
632
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700633 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
635 PackageManager.GET_META_DATA);
636
Bjorn Bringert5f857802010-02-10 23:09:48 +0000637 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 for (int i=0; i<N; i++) {
639 ResolveInfo ri = broadcastReceivers.get(i);
640 addProviderLocked(ri);
641 }
642 }
643
644 boolean addProviderLocked(ResolveInfo ri) {
645 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
646 ri.activityInfo.name), ri);
647 if (p != null) {
648 mInstalledProviders.add(p);
649 return true;
650 } else {
651 return false;
652 }
653 }
654
655 void removeProviderLocked(int index, Provider p) {
656 int N = p.instances.size();
657 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700658 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700660 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 // Stop telling the host about updates for this from now on
662 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700663 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700665 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 id.provider = null;
667 pruneHostLocked(id.host);
668 id.host = null;
669 }
670 p.instances.clear();
671 mInstalledProviders.remove(index);
672 // no need to send the DISABLE broadcast, since the receiver is gone anyway
673 cancelBroadcasts(p);
674 }
675
676 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700677 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 intent.setComponent(p.info.provider);
679 mContext.sendBroadcast(intent);
680 }
681
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700682 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
683 if (appWidgetIds != null && appWidgetIds.length > 0) {
684 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
685 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 intent.setComponent(p.info.provider);
687 mContext.sendBroadcast(intent);
688 }
689 }
690
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700691 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 if (p.info.updatePeriodMillis > 0) {
693 // if this is the first instance, set the alarm. otherwise,
694 // rely on the fact that we've already set it and that
695 // PendingIntent.getBroadcast will update the extras.
696 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700697 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
698 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 intent.setComponent(p.info.provider);
700 long token = Binder.clearCallingIdentity();
701 try {
702 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
703 PendingIntent.FLAG_UPDATE_CURRENT);
704 } finally {
705 Binder.restoreCallingIdentity(token);
706 }
707 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700708 long period = p.info.updatePeriodMillis;
709 if (period < MIN_UPDATE_PERIOD) {
710 period = MIN_UPDATE_PERIOD;
711 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700713 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 }
715 }
716 }
717
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700718 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700720 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700722 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700724 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 }
726
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700727 public int[] getAppWidgetIds(ComponentName provider) {
728 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 Provider p = lookupProviderLocked(provider);
730 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700731 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 } else {
733 return new int[0];
734 }
735 }
736 }
737
738 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
739 Provider p = null;
740
741 ActivityInfo activityInfo = ri.activityInfo;
742 XmlResourceParser parser = null;
743 try {
744 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700745 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800747 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700748 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 return null;
750 }
751
752 AttributeSet attrs = Xml.asAttributeSet(parser);
753
754 int type;
755 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
756 && type != XmlPullParser.START_TAG) {
757 // drain whitespace, comments, etc.
758 }
759
760 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700761 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800762 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700763 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 return null;
765 }
766
767 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700768 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
Romain Guyd2671e12010-03-11 18:06:42 -0800769 // If metaData was null, we would have returned earlier when getting
770 // the parser No need to do the check here
771 info.oldName = activityInfo.metaData.getString(
772 AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773
774 info.provider = component;
775 p.uid = activityInfo.applicationInfo.uid;
776
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800777 Resources res = mPackageManager.getResourcesForApplication(
778 activityInfo.applicationInfo);
779
780 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700781 com.android.internal.R.styleable.AppWidgetProviderInfo);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700782
783 // These dimensions has to be resolved in the application's context.
784 // We simply send back the raw complex data, which will be
785 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
786 TypedValue value = sa.peekValue(
787 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
788 info.minWidth = value != null ? value.data : 0;
789 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
790 info.minHeight = value != null ? value.data : 0;
791
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700793 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700795 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700797 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 if (className != null) {
799 info.configure = new ComponentName(component.getPackageName(), className);
800 }
801 info.label = activityInfo.loadLabel(mPackageManager).toString();
802 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -0700803 info.previewImage = sa.getResourceId(
804 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
805
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 sa.recycle();
807 } catch (Exception e) {
808 // Ok to catch Exception here, because anything going wrong because
809 // of what a client process passes to us should not be fatal for the
810 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800811 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 return null;
813 } finally {
814 if (parser != null) parser.close();
815 }
816 return p;
817 }
818
819 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
820 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
821 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
822 throw new PackageManager.NameNotFoundException();
823 }
824 return pkgInfo.applicationInfo.uid;
825 }
826
827 int enforceCallingUid(String packageName) throws IllegalArgumentException {
828 int callingUid = getCallingUid();
829 int packageUid;
830 try {
831 packageUid = getUidForPackage(packageName);
832 } catch (PackageManager.NameNotFoundException ex) {
833 throw new IllegalArgumentException("packageName and uid don't match packageName="
834 + packageName);
835 }
Marco Nelissen54796e72009-04-30 15:16:30 -0700836 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 throw new IllegalArgumentException("packageName and uid don't match packageName="
838 + packageName);
839 }
840 return callingUid;
841 }
842
843 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700844 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 final int N = mInstalledProviders.size();
846 for (int i=0; i<N; i++) {
847 Provider p = mInstalledProviders.get(i);
848 if (p.instances.size() > 0) {
849 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700850 int[] appWidgetIds = getAppWidgetIds(p);
851 sendUpdateIntentLocked(p, appWidgetIds);
852 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 }
854 }
855 }
856 }
857
858 // only call from initialization -- it assumes that the data structures are all empty
859 void loadStateLocked() {
860 File temp = savedStateTempFile();
861 File real = savedStateRealFile();
862
863 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
864 // real one. if there is both a real file and a temp one, assume that the temp one isn't
865 // fully written and delete it.
866 if (real.exists()) {
867 readStateFromFileLocked(real);
868 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700869 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 temp.delete();
871 }
872 } else if (temp.exists()) {
873 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -0700874 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 temp.renameTo(real);
876 }
877 }
878
879 void saveStateLocked() {
880 File temp = savedStateTempFile();
881 File real = savedStateRealFile();
882
883 if (!real.exists()) {
884 // If the real one doesn't exist, it's either because this is the first time
885 // or because something went wrong while copying them. In this case, we can't
886 // trust anything that's in temp. In order to have the loadState code not
887 // use the temporary one until it's fully written, create an empty file
888 // for real, which will we'll shortly delete.
889 try {
Romain Guya5475592009-07-01 17:20:08 -0700890 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 real.createNewFile();
892 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -0700893 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 }
895 }
896
897 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700898 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 temp.delete();
900 }
901
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700902 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800903 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700904 return;
905 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906
Romain Guya5475592009-07-01 17:20:08 -0700907 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800908 real.delete();
Romain Guya5475592009-07-01 17:20:08 -0700909 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 temp.renameTo(real);
911 }
912
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700913 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 FileOutputStream stream = null;
915 int N;
916
917 try {
918 stream = new FileOutputStream(file, false);
919 XmlSerializer out = new FastXmlSerializer();
920 out.setOutput(stream, "utf-8");
921 out.startDocument(null, true);
922
923
924 out.startTag(null, "gs");
925
926 int providerIndex = 0;
927 N = mInstalledProviders.size();
928 for (int i=0; i<N; i++) {
929 Provider p = mInstalledProviders.get(i);
930 if (p.instances.size() > 0) {
931 out.startTag(null, "p");
932 out.attribute(null, "pkg", p.info.provider.getPackageName());
933 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +0800934 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 p.tag = providerIndex;
936 providerIndex++;
937 }
938 }
939
940 N = mHosts.size();
941 for (int i=0; i<N; i++) {
942 Host host = mHosts.get(i);
943 out.startTag(null, "h");
944 out.attribute(null, "pkg", host.packageName);
945 out.attribute(null, "id", Integer.toHexString(host.hostId));
946 out.endTag(null, "h");
947 host.tag = i;
948 }
949
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700950 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700952 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800953 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700954 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 out.attribute(null, "h", Integer.toHexString(id.host.tag));
956 if (id.provider != null) {
957 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
958 }
959 out.endTag(null, "g");
960 }
961
962 out.endTag(null, "gs");
963
964 out.endDocument();
965 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700966 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 } catch (IOException e) {
968 try {
969 if (stream != null) {
970 stream.close();
971 }
972 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -0700973 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 }
975 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700976 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 file.delete();
978 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700979 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 }
981 }
982
983 void readStateFromFileLocked(File file) {
984 FileInputStream stream = null;
985
986 boolean success = false;
987
988 try {
989 stream = new FileInputStream(file);
990 XmlPullParser parser = Xml.newPullParser();
991 parser.setInput(stream, null);
992
993 int type;
994 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -0700995 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 do {
997 type = parser.next();
998 if (type == XmlPullParser.START_TAG) {
999 String tag = parser.getName();
1000 if ("p".equals(tag)) {
1001 // TODO: do we need to check that this package has the same signature
1002 // as before?
1003 String pkg = parser.getAttributeValue(null, "pkg");
1004 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001005
1006 final PackageManager packageManager = mContext.getPackageManager();
1007 try {
1008 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1009 } catch (PackageManager.NameNotFoundException e) {
1010 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1011 new String[] { pkg });
1012 pkg = pkgs[0];
1013 }
1014
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1016 if (p == null && mSafeMode) {
1017 // if we're in safe mode, make a temporary one
1018 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001019 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 p.info.provider = new ComponentName(pkg, cl);
1021 p.zombie = true;
1022 mInstalledProviders.add(p);
1023 }
1024 if (p != null) {
1025 // if it wasn't uninstalled or something
1026 loadedProviders.put(providerIndex, p);
1027 }
1028 providerIndex++;
1029 }
1030 else if ("h".equals(tag)) {
1031 Host host = new Host();
1032
1033 // TODO: do we need to check that this package has the same signature
1034 // as before?
1035 host.packageName = parser.getAttributeValue(null, "pkg");
1036 try {
1037 host.uid = getUidForPackage(host.packageName);
1038 } catch (PackageManager.NameNotFoundException ex) {
1039 host.zombie = true;
1040 }
1041 if (!host.zombie || mSafeMode) {
1042 // In safe mode, we don't discard the hosts we don't recognize
1043 // so that they're not pruned from our list. Otherwise, we do.
1044 host.hostId = Integer.parseInt(
1045 parser.getAttributeValue(null, "id"), 16);
1046 mHosts.add(host);
1047 }
1048 }
1049 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001050 AppWidgetId id = new AppWidgetId();
1051 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1052 if (id.appWidgetId >= mNextAppWidgetId) {
1053 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 }
1055
1056 String providerString = parser.getAttributeValue(null, "p");
1057 if (providerString != null) {
1058 // there's no provider if it hasn't been bound yet.
1059 // maybe we don't have to save this, but it brings the system
1060 // to the state it was in.
1061 int pIndex = Integer.parseInt(providerString, 16);
1062 id.provider = loadedProviders.get(pIndex);
1063 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001064 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 + pIndex + " which is " + id.provider);
1066 }
1067 if (id.provider == null) {
1068 // This provider is gone. We just let the host figure out
1069 // that this happened when it fails to load it.
1070 continue;
1071 }
1072 }
1073
1074 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1075 id.host = mHosts.get(hIndex);
1076 if (id.host == null) {
1077 // This host is gone.
1078 continue;
1079 }
1080
1081 if (id.provider != null) {
1082 id.provider.instances.add(id);
1083 }
1084 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001085 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 }
1087 }
1088 } while (type != XmlPullParser.END_DOCUMENT);
1089 success = true;
1090 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001091 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001093 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001095 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001097 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001099 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 }
1101 try {
1102 if (stream != null) {
1103 stream.close();
1104 }
1105 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001106 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 }
1108
1109 if (success) {
1110 // delete any hosts that didn't manage to get connected (should happen)
1111 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001112 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 pruneHostLocked(mHosts.get(i));
1114 }
1115 } else {
1116 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001117 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 mHosts.clear();
1119 final int N = mInstalledProviders.size();
1120 for (int i=0; i<N; i++) {
1121 mInstalledProviders.get(i).instances.clear();
1122 }
1123 }
1124 }
1125
1126 File savedStateTempFile() {
1127 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1128 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1129 }
1130
1131 File savedStateRealFile() {
1132 return new File("/data/system/" + SETTINGS_FILENAME);
1133 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1134 }
1135
1136 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1137 public void onReceive(Context context, Intent intent) {
1138 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001139 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1141 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001142 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1143 Locale revised = Locale.getDefault();
1144 if (revised == null || mLocale == null ||
1145 !(revised.equals(mLocale))) {
1146 mLocale = revised;
1147
1148 synchronized (mAppWidgetIds) {
1149 int N = mInstalledProviders.size();
1150 for (int i=N-1; i>=0; i--) {
1151 Provider p = mInstalledProviders.get(i);
1152 String pkgName = p.info.provider.getPackageName();
1153 updateProvidersForPackageLocked(pkgName);
1154 }
1155 saveStateLocked();
1156 }
1157 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001159 boolean added = false;
1160 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001161 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001162 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1163 added = true;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001164 } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001165 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1166 added = false;
1167 } else {
1168 Uri uri = intent.getData();
1169 if (uri == null) {
1170 return;
1171 }
1172 String pkgName = uri.getSchemeSpecificPart();
1173 if (pkgName == null) {
1174 return;
1175 }
1176 pkgList = new String[] { pkgName };
1177 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1178 }
1179 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 return;
1181 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001182 if (added) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001183 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001184 Bundle extras = intent.getExtras();
1185 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001186 for (String pkgName : pkgList) {
1187 // The package was just upgraded
1188 updateProvidersForPackageLocked(pkgName);
1189 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 } else {
1191 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001192 for (String pkgName : pkgList) {
1193 addProvidersForPackageLocked(pkgName);
1194 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 }
1196 saveStateLocked();
1197 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001198 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199 Bundle extras = intent.getExtras();
1200 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1201 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1202 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001203 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001204 for (String pkgName : pkgList) {
1205 removeProvidersForPackageLocked(pkgName);
1206 saveStateLocked();
1207 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 }
1209 }
1210 }
1211 }
1212 }
1213 };
1214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001215 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001216 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001217 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1219 PackageManager.GET_META_DATA);
1220
Bjorn Bringert5f857802010-02-10 23:09:48 +00001221 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001222 for (int i=0; i<N; i++) {
1223 ResolveInfo ri = broadcastReceivers.get(i);
1224 ActivityInfo ai = ri.activityInfo;
1225
1226 if (pkgName.equals(ai.packageName)) {
1227 addProviderLocked(ri);
1228 }
1229 }
1230 }
1231
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001233 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001234 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001235 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1237 PackageManager.GET_META_DATA);
1238
1239 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001240 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 for (int i=0; i<N; i++) {
1242 ResolveInfo ri = broadcastReceivers.get(i);
1243 ActivityInfo ai = ri.activityInfo;
1244 if (pkgName.equals(ai.packageName)) {
1245 ComponentName component = new ComponentName(ai.packageName, ai.name);
1246 Provider p = lookupProviderLocked(component);
1247 if (p == null) {
1248 if (addProviderLocked(ri)) {
1249 keep.add(ai.name);
1250 }
1251 } else {
1252 Provider parsed = parseProviderInfoXml(component, ri);
1253 if (parsed != null) {
1254 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001255 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 p.info = parsed.info;
1257 // If it's enabled
1258 final int M = p.instances.size();
1259 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001260 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 // Reschedule for the new updatePeriodMillis (don't worry about handling
1262 // it specially if updatePeriodMillis didn't change because we just sent
1263 // an update, and the next one will be updatePeriodMillis from now).
1264 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001265 registerForBroadcastsLocked(p, appWidgetIds);
1266 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001268 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001269 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 if (id.host != null && id.host.callbacks != null) {
1271 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001272 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 } catch (RemoteException ex) {
1274 // It failed; remove the callback. No need to prune because
1275 // we know that this host is still referenced by this
1276 // instance.
1277 id.host.callbacks = null;
1278 }
1279 }
1280 }
1281 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001282 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 }
1284 }
1285 }
1286 }
1287 }
1288
1289 // prune the ones we don't want to keep
1290 N = mInstalledProviders.size();
1291 for (int i=N-1; i>=0; i--) {
1292 Provider p = mInstalledProviders.get(i);
1293 if (pkgName.equals(p.info.provider.getPackageName())
1294 && !keep.contains(p.info.provider.getClassName())) {
1295 removeProviderLocked(i, p);
1296 }
1297 }
1298 }
1299
1300 void removeProvidersForPackageLocked(String pkgName) {
1301 int N = mInstalledProviders.size();
1302 for (int i=N-1; i>=0; i--) {
1303 Provider p = mInstalledProviders.get(i);
1304 if (pkgName.equals(p.info.provider.getPackageName())) {
1305 removeProviderLocked(i, p);
1306 }
1307 }
1308
1309 // Delete the hosts for this package too
1310 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001311 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 // so we don't need to worry about sending DISABLE broadcasts to them.
1313 N = mHosts.size();
1314 for (int i=N-1; i>=0; i--) {
1315 Host host = mHosts.get(i);
1316 if (pkgName.equals(host.packageName)) {
1317 deleteHostLocked(host);
1318 }
1319 }
1320 }
1321}
1322