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