blob: f336d1f6dbe0c54021f83e8a772ab59ddbcc0c42 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.res.TypedArray;
33import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.net.Uri;
35import android.os.Binder;
36import android.os.Bundle;
Marco Nelissen54796e72009-04-30 15:16:30 -070037import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.os.RemoteException;
39import android.os.SystemClock;
40import android.util.AttributeSet;
41import android.util.Log;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070042import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.util.Xml;
44import android.widget.RemoteViews;
45
46import java.io.IOException;
47import java.io.File;
48import java.io.FileDescriptor;
49import java.io.FileInputStream;
50import java.io.FileOutputStream;
51import java.io.PrintWriter;
52import java.util.ArrayList;
53import java.util.List;
Eric Fischer63c2d9e2009-10-22 15:22:50 -070054import java.util.Locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import java.util.HashMap;
56import java.util.HashSet;
57
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070058import com.android.internal.appwidget.IAppWidgetService;
59import com.android.internal.appwidget.IAppWidgetHost;
Tom Taylord4a47292009-12-21 13:59:18 -080060import com.android.common.FastXmlSerializer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
62import org.xmlpull.v1.XmlPullParser;
63import org.xmlpull.v1.XmlPullParserException;
64import org.xmlpull.v1.XmlSerializer;
65
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070066class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070068 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070070 private static final String SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070072 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
74 /*
75 * When identifying a Host or Provider based on the calling process, use the uid field.
76 * When identifying a Host or Provider based on a package manager broadcast, use the
77 * package given.
78 */
79
80 static class Provider {
81 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070082 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070083 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 PendingIntent broadcast;
85 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
86
87 int tag; // for use while saving state (the index)
88 }
89
90 static class Host {
91 int uid;
92 int hostId;
93 String packageName;
Romain Guya5475592009-07-01 17:20:08 -070094 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070095 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
97
98 int tag; // for use while saving state (the index)
99 }
100
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700101 static class AppWidgetId {
102 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 Provider provider;
104 RemoteViews views;
105 Host host;
106 }
107
108 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700109 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 PackageManager mPackageManager;
111 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700112 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700113 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700114 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
115 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 boolean mSafeMode;
117
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700118 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 mContext = context;
120 mPackageManager = context.getPackageManager();
121 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
122 }
123
124 public void systemReady(boolean safeMode) {
125 mSafeMode = safeMode;
126
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700127 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 loadStateLocked();
129
130 // Register for the boot completed broadcast, so we can send the
131 // ENABLE broacasts. If we try to send them now, they time out,
132 // because the system isn't ready to handle them yet.
133 mContext.registerReceiver(mBroadcastReceiver,
134 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
135
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700136 // Register for configuration changes so we can update the names
137 // of the widgets when the locale changes.
138 mContext.registerReceiver(mBroadcastReceiver,
139 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 // Register for broadcasts about package install, etc., so we can
142 // update the provider list.
143 IntentFilter filter = new IntentFilter();
144 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
145 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
146 filter.addDataScheme("package");
147 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800148 // Register for events related to sdcard installation.
149 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800150 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
151 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800152 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 }
154
155 @Override
156 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
157 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
158 != PackageManager.PERMISSION_GRANTED) {
159 pw.println("Permission Denial: can't dump from from pid="
160 + Binder.getCallingPid()
161 + ", uid=" + Binder.getCallingUid());
162 return;
163 }
164
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700165 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700167 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 for (int i=0; i<N; i++) {
169 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700170 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700171 pw.print(" ["); pw.print(i); pw.print("] provider ");
172 pw.print(info.provider.flattenToShortString());
173 pw.println(':');
174 pw.print(" min=("); pw.print(info.minWidth);
175 pw.print("x"); pw.print(info.minHeight);
176 pw.print(") updatePeriodMillis=");
177 pw.print(info.updatePeriodMillis);
178 pw.print(" initialLayout=#");
179 pw.print(Integer.toHexString(info.initialLayout));
180 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 }
182
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700183 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700184 pw.println(" ");
185 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700187 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700188 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700189 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700190 pw.print(" hostId=");
191 pw.print(id.host.hostId); pw.print(' ');
192 pw.print(id.host.packageName); pw.print('/');
193 pw.println(id.host.uid);
194 if (id.provider != null) {
195 pw.print(" provider=");
196 pw.println(id.provider.info.provider.flattenToShortString());
197 }
198 if (id.host != null) {
199 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
200 }
201 if (id.views != null) {
202 pw.print(" views="); pw.println(id.views);
203 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 }
205
206 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700207 pw.println(" ");
208 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 for (int i=0; i<N; i++) {
210 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700211 pw.print(" ["); pw.print(i); pw.print("] hostId=");
212 pw.print(host.hostId); pw.print(' ');
213 pw.print(host.packageName); pw.print('/');
214 pw.print(host.uid); pw.println(':');
215 pw.print(" callbacks="); pw.println(host.callbacks);
216 pw.print(" instances.size="); pw.print(host.instances.size());
217 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 }
219 }
220 }
221
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700222 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700224 synchronized (mAppWidgetIds) {
225 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226
227 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
228
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700229 AppWidgetId id = new AppWidgetId();
230 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 id.host = host;
232
233 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700234 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235
236 saveStateLocked();
237
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700238 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 }
240 }
241
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700242 public void deleteAppWidgetId(int appWidgetId) {
243 synchronized (mAppWidgetIds) {
244 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700246 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 saveStateLocked();
248 }
249 }
250 }
251
252 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700253 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 int callingUid = getCallingUid();
255 Host host = lookupHostLocked(callingUid, hostId);
256 if (host != null) {
257 deleteHostLocked(host);
258 saveStateLocked();
259 }
260 }
261 }
262
263 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700264 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 int callingUid = getCallingUid();
266 final int N = mHosts.size();
267 boolean changed = false;
268 for (int i=N-1; i>=0; i--) {
269 Host host = mHosts.get(i);
270 if (host.uid == callingUid) {
271 deleteHostLocked(host);
272 changed = true;
273 }
274 }
275 if (changed) {
276 saveStateLocked();
277 }
278 }
279 }
280
281 void deleteHostLocked(Host host) {
282 final int N = host.instances.size();
283 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700284 AppWidgetId id = host.instances.get(i);
285 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 }
287 host.instances.clear();
288 mHosts.remove(host);
289 // it's gone or going away, abruptly drop the callback connection
290 host.callbacks = null;
291 }
292
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700293 void deleteAppWidgetLocked(AppWidgetId id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 Host host = id.host;
295 host.instances.remove(id);
296 pruneHostLocked(host);
297
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700298 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299
300 Provider p = id.provider;
301 if (p != null) {
302 p.instances.remove(id);
303 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700304 // send the broacast saying that this appWidgetId has been deleted
305 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700307 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 mContext.sendBroadcast(intent);
309 if (p.instances.size() == 0) {
310 // cancel the future updates
311 cancelBroadcasts(p);
312
313 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700314 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 intent.setComponent(p.info.provider);
316 mContext.sendBroadcast(intent);
317 }
318 }
319 }
320 }
321
322 void cancelBroadcasts(Provider p) {
323 if (p.broadcast != null) {
324 mAlarmManager.cancel(p.broadcast);
325 long token = Binder.clearCallingIdentity();
326 try {
327 p.broadcast.cancel();
328 } finally {
329 Binder.restoreCallingIdentity(token);
330 }
331 p.broadcast = null;
332 }
333 }
334
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700335 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
336 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
337 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
338 synchronized (mAppWidgetIds) {
339 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700341 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 }
343 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700344 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 + id.provider.info.provider);
346 }
347 Provider p = lookupProviderLocked(provider);
348 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700349 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 }
351 if (p.zombie) {
352 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
353 + " safe mode: " + provider);
354 }
355
356 id.provider = p;
357 p.instances.add(id);
358 int instancesSize = p.instances.size();
359 if (instancesSize == 1) {
360 // tell the provider that it's ready
361 sendEnableIntentLocked(p);
362 }
363
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700364 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 // It's less critical when the next one happens, so when we schdule the next one,
366 // we add updatePeriodMillis to its start time. That time will have some slop,
367 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700368 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369
370 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700371 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 saveStateLocked();
373 }
374 }
375
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700376 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
377 synchronized (mAppWidgetIds) {
378 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 if (id != null && id.provider != null && !id.provider.zombie) {
380 return id.provider.info;
381 }
382 return null;
383 }
384 }
385
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700386 public RemoteViews getAppWidgetViews(int appWidgetId) {
387 synchronized (mAppWidgetIds) {
388 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 if (id != null) {
390 return id.views;
391 }
392 return null;
393 }
394 }
395
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700396 public List<AppWidgetProviderInfo> getInstalledProviders() {
397 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700399 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 for (int i=0; i<N; i++) {
401 Provider p = mInstalledProviders.get(i);
402 if (!p.zombie) {
403 result.add(p.info);
404 }
405 }
406 return result;
407 }
408 }
409
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700410 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
411 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 return;
413 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700414 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 return;
416 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700417 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700419 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700421 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
422 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 }
424 }
425 }
426
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700427 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
428 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 Provider p = lookupProviderLocked(provider);
430 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700431 Log.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 return;
433 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700434 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 final int N = instances.size();
436 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700437 AppWidgetId id = instances.get(i);
438 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 }
440 }
441 }
442
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700443 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
444 // allow for stale appWidgetIds and other badness
445 // lookup also checks that the calling process can access the appWidgetId
446 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
448 id.views = views;
449
450 // is anyone listening?
451 if (id.host.callbacks != null) {
452 try {
453 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700454 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 } catch (RemoteException e) {
456 // It failed; remove the callback. No need to prune because
457 // we know that this host is still referenced by this instance.
458 id.host.callbacks = null;
459 }
460 }
461 }
462 }
463
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700464 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 List<RemoteViews> updatedViews) {
466 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700467 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
469 host.callbacks = callbacks;
470
471 updatedViews.clear();
472
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700473 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 int N = instances.size();
475 int[] updatedIds = new int[N];
476 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700477 AppWidgetId id = instances.get(i);
478 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 updatedViews.add(id.views);
480 }
481 return updatedIds;
482 }
483 }
484
485 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700486 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700488 if (host != null) {
489 host.callbacks = null;
490 pruneHostLocked(host);
491 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 }
493 }
494
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700495 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700497 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 return true;
499 }
500 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700501 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 return true;
503 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700504 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700506 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 return true;
508 }
509 // Nobody else can access it.
510 return false;
511 }
512
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700513 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700515 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700517 AppWidgetId id = mAppWidgetIds.get(i);
518 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 return id;
520 }
521 }
522 return null;
523 }
524
525 Provider lookupProviderLocked(ComponentName provider) {
526 final int N = mInstalledProviders.size();
527 for (int i=0; i<N; i++) {
528 Provider p = mInstalledProviders.get(i);
529 if (p.info.provider.equals(provider)) {
530 return p;
531 }
532 }
533 return null;
534 }
535
536 Host lookupHostLocked(int uid, int hostId) {
537 final int N = mHosts.size();
538 for (int i=0; i<N; i++) {
539 Host h = mHosts.get(i);
540 if (h.uid == uid && h.hostId == hostId) {
541 return h;
542 }
543 }
544 return null;
545 }
546
547 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
548 final int N = mHosts.size();
549 for (int i=0; i<N; i++) {
550 Host h = mHosts.get(i);
551 if (h.hostId == hostId && h.packageName.equals(packageName)) {
552 return h;
553 }
554 }
555 Host host = new Host();
556 host.packageName = packageName;
557 host.uid = uid;
558 host.hostId = hostId;
559 mHosts.add(host);
560 return host;
561 }
562
563 void pruneHostLocked(Host host) {
564 if (host.instances.size() == 0 && host.callbacks == null) {
565 mHosts.remove(host);
566 }
567 }
568
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700569 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 PackageManager pm = mPackageManager;
571
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700572 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
574 PackageManager.GET_META_DATA);
575
576 final int N = broadcastReceivers.size();
577 for (int i=0; i<N; i++) {
578 ResolveInfo ri = broadcastReceivers.get(i);
579 addProviderLocked(ri);
580 }
581 }
582
583 boolean addProviderLocked(ResolveInfo ri) {
584 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
585 ri.activityInfo.name), ri);
586 if (p != null) {
587 mInstalledProviders.add(p);
588 return true;
589 } else {
590 return false;
591 }
592 }
593
594 void removeProviderLocked(int index, Provider p) {
595 int N = p.instances.size();
596 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700597 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700599 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 // Stop telling the host about updates for this from now on
601 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700602 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700604 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 id.provider = null;
606 pruneHostLocked(id.host);
607 id.host = null;
608 }
609 p.instances.clear();
610 mInstalledProviders.remove(index);
611 // no need to send the DISABLE broadcast, since the receiver is gone anyway
612 cancelBroadcasts(p);
613 }
614
615 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700616 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 intent.setComponent(p.info.provider);
618 mContext.sendBroadcast(intent);
619 }
620
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700621 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
622 if (appWidgetIds != null && appWidgetIds.length > 0) {
623 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
624 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 intent.setComponent(p.info.provider);
626 mContext.sendBroadcast(intent);
627 }
628 }
629
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700630 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 if (p.info.updatePeriodMillis > 0) {
632 // if this is the first instance, set the alarm. otherwise,
633 // rely on the fact that we've already set it and that
634 // PendingIntent.getBroadcast will update the extras.
635 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700636 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
637 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 intent.setComponent(p.info.provider);
639 long token = Binder.clearCallingIdentity();
640 try {
641 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
642 PendingIntent.FLAG_UPDATE_CURRENT);
643 } finally {
644 Binder.restoreCallingIdentity(token);
645 }
646 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700647 long period = p.info.updatePeriodMillis;
648 if (period < MIN_UPDATE_PERIOD) {
649 period = MIN_UPDATE_PERIOD;
650 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700652 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 }
654 }
655 }
656
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700657 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700659 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700661 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700663 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 }
665
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700666 public int[] getAppWidgetIds(ComponentName provider) {
667 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 Provider p = lookupProviderLocked(provider);
669 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700670 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 } else {
672 return new int[0];
673 }
674 }
675 }
676
677 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
678 Provider p = null;
679
680 ActivityInfo activityInfo = ri.activityInfo;
681 XmlResourceParser parser = null;
682 try {
683 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700684 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 if (parser == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700686 Log.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
687 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 return null;
689 }
690
691 AttributeSet attrs = Xml.asAttributeSet(parser);
692
693 int type;
694 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
695 && type != XmlPullParser.START_TAG) {
696 // drain whitespace, comments, etc.
697 }
698
699 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700700 if (!"appwidget-provider".equals(nodeName)) {
701 Log.w(TAG, "Meta-data does not start with appwidget-provider tag for"
702 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 return null;
704 }
705
706 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700707 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708
709 info.provider = component;
710 p.uid = activityInfo.applicationInfo.uid;
711
712 TypedArray sa = mContext.getResources().obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700713 com.android.internal.R.styleable.AppWidgetProviderInfo);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700714
715 // These dimensions has to be resolved in the application's context.
716 // We simply send back the raw complex data, which will be
717 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
718 TypedValue value = sa.peekValue(
719 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
720 info.minWidth = value != null ? value.data : 0;
721 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
722 info.minHeight = value != null ? value.data : 0;
723
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700725 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700727 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700729 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 if (className != null) {
731 info.configure = new ComponentName(component.getPackageName(), className);
732 }
733 info.label = activityInfo.loadLabel(mPackageManager).toString();
734 info.icon = ri.getIconResource();
735 sa.recycle();
736 } catch (Exception e) {
737 // Ok to catch Exception here, because anything going wrong because
738 // of what a client process passes to us should not be fatal for the
739 // system process.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700740 Log.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 return null;
742 } finally {
743 if (parser != null) parser.close();
744 }
745 return p;
746 }
747
748 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
749 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
750 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
751 throw new PackageManager.NameNotFoundException();
752 }
753 return pkgInfo.applicationInfo.uid;
754 }
755
756 int enforceCallingUid(String packageName) throws IllegalArgumentException {
757 int callingUid = getCallingUid();
758 int packageUid;
759 try {
760 packageUid = getUidForPackage(packageName);
761 } catch (PackageManager.NameNotFoundException ex) {
762 throw new IllegalArgumentException("packageName and uid don't match packageName="
763 + packageName);
764 }
Marco Nelissen54796e72009-04-30 15:16:30 -0700765 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 throw new IllegalArgumentException("packageName and uid don't match packageName="
767 + packageName);
768 }
769 return callingUid;
770 }
771
772 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700773 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 final int N = mInstalledProviders.size();
775 for (int i=0; i<N; i++) {
776 Provider p = mInstalledProviders.get(i);
777 if (p.instances.size() > 0) {
778 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700779 int[] appWidgetIds = getAppWidgetIds(p);
780 sendUpdateIntentLocked(p, appWidgetIds);
781 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 }
783 }
784 }
785 }
786
787 // only call from initialization -- it assumes that the data structures are all empty
788 void loadStateLocked() {
789 File temp = savedStateTempFile();
790 File real = savedStateRealFile();
791
792 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
793 // real one. if there is both a real file and a temp one, assume that the temp one isn't
794 // fully written and delete it.
795 if (real.exists()) {
796 readStateFromFileLocked(real);
797 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700798 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 temp.delete();
800 }
801 } else if (temp.exists()) {
802 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -0700803 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 temp.renameTo(real);
805 }
806 }
807
808 void saveStateLocked() {
809 File temp = savedStateTempFile();
810 File real = savedStateRealFile();
811
812 if (!real.exists()) {
813 // If the real one doesn't exist, it's either because this is the first time
814 // or because something went wrong while copying them. In this case, we can't
815 // trust anything that's in temp. In order to have the loadState code not
816 // use the temporary one until it's fully written, create an empty file
817 // for real, which will we'll shortly delete.
818 try {
Romain Guya5475592009-07-01 17:20:08 -0700819 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820 real.createNewFile();
821 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -0700822 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 }
824 }
825
826 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700827 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 temp.delete();
829 }
830
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700831 if (!writeStateToFileLocked(temp)) {
832 Log.w(TAG, "Failed to persist new settings");
833 return;
834 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835
Romain Guya5475592009-07-01 17:20:08 -0700836 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 real.delete();
Romain Guya5475592009-07-01 17:20:08 -0700838 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 temp.renameTo(real);
840 }
841
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700842 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 FileOutputStream stream = null;
844 int N;
845
846 try {
847 stream = new FileOutputStream(file, false);
848 XmlSerializer out = new FastXmlSerializer();
849 out.setOutput(stream, "utf-8");
850 out.startDocument(null, true);
851
852
853 out.startTag(null, "gs");
854
855 int providerIndex = 0;
856 N = mInstalledProviders.size();
857 for (int i=0; i<N; i++) {
858 Provider p = mInstalledProviders.get(i);
859 if (p.instances.size() > 0) {
860 out.startTag(null, "p");
861 out.attribute(null, "pkg", p.info.provider.getPackageName());
862 out.attribute(null, "cl", p.info.provider.getClassName());
863 out.endTag(null, "h");
864 p.tag = providerIndex;
865 providerIndex++;
866 }
867 }
868
869 N = mHosts.size();
870 for (int i=0; i<N; i++) {
871 Host host = mHosts.get(i);
872 out.startTag(null, "h");
873 out.attribute(null, "pkg", host.packageName);
874 out.attribute(null, "id", Integer.toHexString(host.hostId));
875 out.endTag(null, "h");
876 host.tag = i;
877 }
878
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700879 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800880 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700881 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700883 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 out.attribute(null, "h", Integer.toHexString(id.host.tag));
885 if (id.provider != null) {
886 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
887 }
888 out.endTag(null, "g");
889 }
890
891 out.endTag(null, "gs");
892
893 out.endDocument();
894 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700895 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 } catch (IOException e) {
897 try {
898 if (stream != null) {
899 stream.close();
900 }
901 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -0700902 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 }
904 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700905 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 file.delete();
907 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700908 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 }
910 }
911
912 void readStateFromFileLocked(File file) {
913 FileInputStream stream = null;
914
915 boolean success = false;
916
917 try {
918 stream = new FileInputStream(file);
919 XmlPullParser parser = Xml.newPullParser();
920 parser.setInput(stream, null);
921
922 int type;
923 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -0700924 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 do {
926 type = parser.next();
927 if (type == XmlPullParser.START_TAG) {
928 String tag = parser.getName();
929 if ("p".equals(tag)) {
930 // TODO: do we need to check that this package has the same signature
931 // as before?
932 String pkg = parser.getAttributeValue(null, "pkg");
933 String cl = parser.getAttributeValue(null, "cl");
934 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
935 if (p == null && mSafeMode) {
936 // if we're in safe mode, make a temporary one
937 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700938 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 p.info.provider = new ComponentName(pkg, cl);
940 p.zombie = true;
941 mInstalledProviders.add(p);
942 }
943 if (p != null) {
944 // if it wasn't uninstalled or something
945 loadedProviders.put(providerIndex, p);
946 }
947 providerIndex++;
948 }
949 else if ("h".equals(tag)) {
950 Host host = new Host();
951
952 // TODO: do we need to check that this package has the same signature
953 // as before?
954 host.packageName = parser.getAttributeValue(null, "pkg");
955 try {
956 host.uid = getUidForPackage(host.packageName);
957 } catch (PackageManager.NameNotFoundException ex) {
958 host.zombie = true;
959 }
960 if (!host.zombie || mSafeMode) {
961 // In safe mode, we don't discard the hosts we don't recognize
962 // so that they're not pruned from our list. Otherwise, we do.
963 host.hostId = Integer.parseInt(
964 parser.getAttributeValue(null, "id"), 16);
965 mHosts.add(host);
966 }
967 }
968 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700969 AppWidgetId id = new AppWidgetId();
970 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
971 if (id.appWidgetId >= mNextAppWidgetId) {
972 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 }
974
975 String providerString = parser.getAttributeValue(null, "p");
976 if (providerString != null) {
977 // there's no provider if it hasn't been bound yet.
978 // maybe we don't have to save this, but it brings the system
979 // to the state it was in.
980 int pIndex = Integer.parseInt(providerString, 16);
981 id.provider = loadedProviders.get(pIndex);
982 if (false) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700983 Log.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 + pIndex + " which is " + id.provider);
985 }
986 if (id.provider == null) {
987 // This provider is gone. We just let the host figure out
988 // that this happened when it fails to load it.
989 continue;
990 }
991 }
992
993 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
994 id.host = mHosts.get(hIndex);
995 if (id.host == null) {
996 // This host is gone.
997 continue;
998 }
999
1000 if (id.provider != null) {
1001 id.provider.instances.add(id);
1002 }
1003 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001004 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001005 }
1006 }
1007 } while (type != XmlPullParser.END_DOCUMENT);
1008 success = true;
1009 } catch (NullPointerException e) {
1010 Log.w(TAG, "failed parsing " + file, e);
1011 } catch (NumberFormatException e) {
1012 Log.w(TAG, "failed parsing " + file, e);
1013 } catch (XmlPullParserException e) {
1014 Log.w(TAG, "failed parsing " + file, e);
1015 } catch (IOException e) {
1016 Log.w(TAG, "failed parsing " + file, e);
1017 } catch (IndexOutOfBoundsException e) {
1018 Log.w(TAG, "failed parsing " + file, e);
1019 }
1020 try {
1021 if (stream != null) {
1022 stream.close();
1023 }
1024 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001025 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 }
1027
1028 if (success) {
1029 // delete any hosts that didn't manage to get connected (should happen)
1030 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001031 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 pruneHostLocked(mHosts.get(i));
1033 }
1034 } else {
1035 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001036 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 mHosts.clear();
1038 final int N = mInstalledProviders.size();
1039 for (int i=0; i<N; i++) {
1040 mInstalledProviders.get(i).instances.clear();
1041 }
1042 }
1043 }
1044
1045 File savedStateTempFile() {
1046 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1047 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1048 }
1049
1050 File savedStateRealFile() {
1051 return new File("/data/system/" + SETTINGS_FILENAME);
1052 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1053 }
1054
1055 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1056 public void onReceive(Context context, Intent intent) {
1057 String action = intent.getAction();
1058 //Log.d(TAG, "received " + action);
1059 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1060 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001061 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1062 Locale revised = Locale.getDefault();
1063 if (revised == null || mLocale == null ||
1064 !(revised.equals(mLocale))) {
1065 mLocale = revised;
1066
1067 synchronized (mAppWidgetIds) {
1068 int N = mInstalledProviders.size();
1069 for (int i=N-1; i>=0; i--) {
1070 Provider p = mInstalledProviders.get(i);
1071 String pkgName = p.info.provider.getPackageName();
1072 updateProvidersForPackageLocked(pkgName);
1073 }
1074 saveStateLocked();
1075 }
1076 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001078 boolean added = false;
1079 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001080 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001081 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1082 added = true;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001083 } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001084 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1085 added = false;
1086 } else {
1087 Uri uri = intent.getData();
1088 if (uri == null) {
1089 return;
1090 }
1091 String pkgName = uri.getSchemeSpecificPart();
1092 if (pkgName == null) {
1093 return;
1094 }
1095 pkgList = new String[] { pkgName };
1096 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1097 }
1098 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 return;
1100 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001101 if (added) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001102 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 Bundle extras = intent.getExtras();
1104 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001105 for (String pkgName : pkgList) {
1106 // The package was just upgraded
1107 updateProvidersForPackageLocked(pkgName);
1108 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 } else {
1110 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001111 for (String pkgName : pkgList) {
1112 addProvidersForPackageLocked(pkgName);
1113 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 }
1115 saveStateLocked();
1116 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001117 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 Bundle extras = intent.getExtras();
1119 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1120 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1121 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001122 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001123 for (String pkgName : pkgList) {
1124 removeProvidersForPackageLocked(pkgName);
1125 saveStateLocked();
1126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 }
1128 }
1129 }
1130 }
1131 }
1132 };
1133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001135 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001136 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1138 PackageManager.GET_META_DATA);
1139
1140 final int N = broadcastReceivers.size();
1141 for (int i=0; i<N; i++) {
1142 ResolveInfo ri = broadcastReceivers.get(i);
1143 ActivityInfo ai = ri.activityInfo;
1144
1145 if (pkgName.equals(ai.packageName)) {
1146 addProviderLocked(ri);
1147 }
1148 }
1149 }
1150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001152 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001153 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001154 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001155 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1156 PackageManager.GET_META_DATA);
1157
1158 // add the missing ones and collect which ones to keep
1159 int N = broadcastReceivers.size();
1160 for (int i=0; i<N; i++) {
1161 ResolveInfo ri = broadcastReceivers.get(i);
1162 ActivityInfo ai = ri.activityInfo;
1163 if (pkgName.equals(ai.packageName)) {
1164 ComponentName component = new ComponentName(ai.packageName, ai.name);
1165 Provider p = lookupProviderLocked(component);
1166 if (p == null) {
1167 if (addProviderLocked(ri)) {
1168 keep.add(ai.name);
1169 }
1170 } else {
1171 Provider parsed = parseProviderInfoXml(component, ri);
1172 if (parsed != null) {
1173 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001174 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 p.info = parsed.info;
1176 // If it's enabled
1177 final int M = p.instances.size();
1178 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001179 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 // Reschedule for the new updatePeriodMillis (don't worry about handling
1181 // it specially if updatePeriodMillis didn't change because we just sent
1182 // an update, and the next one will be updatePeriodMillis from now).
1183 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001184 registerForBroadcastsLocked(p, appWidgetIds);
1185 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001187 AppWidgetId id = p.instances.get(j);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 if (id.host != null && id.host.callbacks != null) {
1189 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001190 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 } catch (RemoteException ex) {
1192 // It failed; remove the callback. No need to prune because
1193 // we know that this host is still referenced by this
1194 // instance.
1195 id.host.callbacks = null;
1196 }
1197 }
1198 }
1199 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001200 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 }
1202 }
1203 }
1204 }
1205 }
1206
1207 // prune the ones we don't want to keep
1208 N = mInstalledProviders.size();
1209 for (int i=N-1; i>=0; i--) {
1210 Provider p = mInstalledProviders.get(i);
1211 if (pkgName.equals(p.info.provider.getPackageName())
1212 && !keep.contains(p.info.provider.getClassName())) {
1213 removeProviderLocked(i, p);
1214 }
1215 }
1216 }
1217
1218 void removeProvidersForPackageLocked(String pkgName) {
1219 int N = mInstalledProviders.size();
1220 for (int i=N-1; i>=0; i--) {
1221 Provider p = mInstalledProviders.get(i);
1222 if (pkgName.equals(p.info.provider.getPackageName())) {
1223 removeProviderLocked(i, p);
1224 }
1225 }
1226
1227 // Delete the hosts for this package too
1228 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001229 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 // so we don't need to worry about sending DISABLE broadcasts to them.
1231 N = mHosts.size();
1232 for (int i=N-1; i>=0; i--) {
1233 Host host = mHosts.get(i);
1234 if (pkgName.equals(host.packageName)) {
1235 deleteHostLocked(host);
1236 }
1237 }
1238 }
1239}
1240