blob: f8b8ecc91a0a6b7ca1d54ac751fe62476c4f5bb4 [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";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070071 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
73 /*
74 * When identifying a Host or Provider based on the calling process, use the uid field.
75 * When identifying a Host or Provider based on a package manager broadcast, use the
76 * package given.
77 */
78
79 static class Provider {
80 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070081 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070082 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 PendingIntent broadcast;
84 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
85
86 int tag; // for use while saving state (the index)
87 }
88
89 static class Host {
90 int uid;
91 int hostId;
92 String packageName;
Romain Guya5475592009-07-01 17:20:08 -070093 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070094 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
96
97 int tag; // for use while saving state (the index)
98 }
99
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700100 static class AppWidgetId {
101 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 Provider provider;
103 RemoteViews views;
104 Host host;
105 }
106
107 Context mContext;
108 PackageManager mPackageManager;
109 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700110 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700111 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700112 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
113 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 boolean mSafeMode;
115
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700116 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 mContext = context;
118 mPackageManager = context.getPackageManager();
119 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
120 }
121
122 public void systemReady(boolean safeMode) {
123 mSafeMode = safeMode;
124
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700125 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 loadStateLocked();
127
128 // Register for the boot completed broadcast, so we can send the
129 // ENABLE broacasts. If we try to send them now, they time out,
130 // because the system isn't ready to handle them yet.
131 mContext.registerReceiver(mBroadcastReceiver,
132 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
133
134 // Register for broadcasts about package install, etc., so we can
135 // update the provider list.
136 IntentFilter filter = new IntentFilter();
137 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
138 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
139 filter.addDataScheme("package");
140 mContext.registerReceiver(mBroadcastReceiver, filter);
141 }
142
143 @Override
144 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
145 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
146 != PackageManager.PERMISSION_GRANTED) {
147 pw.println("Permission Denial: can't dump from from pid="
148 + Binder.getCallingPid()
149 + ", uid=" + Binder.getCallingUid());
150 return;
151 }
152
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700153 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700155 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 for (int i=0; i<N; i++) {
157 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700158 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700159 pw.print(" ["); pw.print(i); pw.print("] provider ");
160 pw.print(info.provider.flattenToShortString());
161 pw.println(':');
162 pw.print(" min=("); pw.print(info.minWidth);
163 pw.print("x"); pw.print(info.minHeight);
164 pw.print(") updatePeriodMillis=");
165 pw.print(info.updatePeriodMillis);
166 pw.print(" initialLayout=#");
167 pw.print(Integer.toHexString(info.initialLayout));
168 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 }
170
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700171 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700172 pw.println(" ");
173 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700175 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700176 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700177 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700178 pw.print(" hostId=");
179 pw.print(id.host.hostId); pw.print(' ');
180 pw.print(id.host.packageName); pw.print('/');
181 pw.println(id.host.uid);
182 if (id.provider != null) {
183 pw.print(" provider=");
184 pw.println(id.provider.info.provider.flattenToShortString());
185 }
186 if (id.host != null) {
187 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
188 }
189 if (id.views != null) {
190 pw.print(" views="); pw.println(id.views);
191 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 }
193
194 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700195 pw.println(" ");
196 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 for (int i=0; i<N; i++) {
198 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700199 pw.print(" ["); pw.print(i); pw.print("] hostId=");
200 pw.print(host.hostId); pw.print(' ');
201 pw.print(host.packageName); pw.print('/');
202 pw.print(host.uid); pw.println(':');
203 pw.print(" callbacks="); pw.println(host.callbacks);
204 pw.print(" instances.size="); pw.print(host.instances.size());
205 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
207 }
208 }
209
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700210 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700212 synchronized (mAppWidgetIds) {
213 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214
215 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
216
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700217 AppWidgetId id = new AppWidgetId();
218 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 id.host = host;
220
221 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700222 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223
224 saveStateLocked();
225
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700226 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 }
228 }
229
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700230 public void deleteAppWidgetId(int appWidgetId) {
231 synchronized (mAppWidgetIds) {
232 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700234 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 saveStateLocked();
236 }
237 }
238 }
239
240 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700241 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 int callingUid = getCallingUid();
243 Host host = lookupHostLocked(callingUid, hostId);
244 if (host != null) {
245 deleteHostLocked(host);
246 saveStateLocked();
247 }
248 }
249 }
250
251 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700252 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 int callingUid = getCallingUid();
254 final int N = mHosts.size();
255 boolean changed = false;
256 for (int i=N-1; i>=0; i--) {
257 Host host = mHosts.get(i);
258 if (host.uid == callingUid) {
259 deleteHostLocked(host);
260 changed = true;
261 }
262 }
263 if (changed) {
264 saveStateLocked();
265 }
266 }
267 }
268
269 void deleteHostLocked(Host host) {
270 final int N = host.instances.size();
271 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700272 AppWidgetId id = host.instances.get(i);
273 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 }
275 host.instances.clear();
276 mHosts.remove(host);
277 // it's gone or going away, abruptly drop the callback connection
278 host.callbacks = null;
279 }
280
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700281 void deleteAppWidgetLocked(AppWidgetId id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 Host host = id.host;
283 host.instances.remove(id);
284 pruneHostLocked(host);
285
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700286 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287
288 Provider p = id.provider;
289 if (p != null) {
290 p.instances.remove(id);
291 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700292 // send the broacast saying that this appWidgetId has been deleted
293 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700295 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 mContext.sendBroadcast(intent);
297 if (p.instances.size() == 0) {
298 // cancel the future updates
299 cancelBroadcasts(p);
300
301 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700302 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 intent.setComponent(p.info.provider);
304 mContext.sendBroadcast(intent);
305 }
306 }
307 }
308 }
309
310 void cancelBroadcasts(Provider p) {
311 if (p.broadcast != null) {
312 mAlarmManager.cancel(p.broadcast);
313 long token = Binder.clearCallingIdentity();
314 try {
315 p.broadcast.cancel();
316 } finally {
317 Binder.restoreCallingIdentity(token);
318 }
319 p.broadcast = null;
320 }
321 }
322
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700323 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
324 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
325 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
326 synchronized (mAppWidgetIds) {
327 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700329 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 }
331 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700332 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 + id.provider.info.provider);
334 }
335 Provider p = lookupProviderLocked(provider);
336 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700337 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 }
339 if (p.zombie) {
340 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
341 + " safe mode: " + provider);
342 }
343
344 id.provider = p;
345 p.instances.add(id);
346 int instancesSize = p.instances.size();
347 if (instancesSize == 1) {
348 // tell the provider that it's ready
349 sendEnableIntentLocked(p);
350 }
351
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700352 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 // It's less critical when the next one happens, so when we schdule the next one,
354 // we add updatePeriodMillis to its start time. That time will have some slop,
355 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700356 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357
358 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700359 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 saveStateLocked();
361 }
362 }
363
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700364 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
365 synchronized (mAppWidgetIds) {
366 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 if (id != null && id.provider != null && !id.provider.zombie) {
368 return id.provider.info;
369 }
370 return null;
371 }
372 }
373
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700374 public RemoteViews getAppWidgetViews(int appWidgetId) {
375 synchronized (mAppWidgetIds) {
376 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 if (id != null) {
378 return id.views;
379 }
380 return null;
381 }
382 }
383
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700384 public List<AppWidgetProviderInfo> getInstalledProviders() {
385 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700387 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 for (int i=0; i<N; i++) {
389 Provider p = mInstalledProviders.get(i);
390 if (!p.zombie) {
391 result.add(p.info);
392 }
393 }
394 return result;
395 }
396 }
397
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700398 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
399 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 return;
401 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700402 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 return;
404 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700405 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700407 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700409 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
410 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 }
412 }
413 }
414
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700415 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
416 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 Provider p = lookupProviderLocked(provider);
418 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700419 Log.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 return;
421 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700422 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 final int N = instances.size();
424 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700425 AppWidgetId id = instances.get(i);
426 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 }
428 }
429 }
430
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700431 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
432 // allow for stale appWidgetIds and other badness
433 // lookup also checks that the calling process can access the appWidgetId
434 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
436 id.views = views;
437
438 // is anyone listening?
439 if (id.host.callbacks != null) {
440 try {
441 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700442 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 } catch (RemoteException e) {
444 // It failed; remove the callback. No need to prune because
445 // we know that this host is still referenced by this instance.
446 id.host.callbacks = null;
447 }
448 }
449 }
450 }
451
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700452 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 List<RemoteViews> updatedViews) {
454 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700455 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
457 host.callbacks = callbacks;
458
459 updatedViews.clear();
460
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700461 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 int N = instances.size();
463 int[] updatedIds = new int[N];
464 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700465 AppWidgetId id = instances.get(i);
466 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 updatedViews.add(id.views);
468 }
469 return updatedIds;
470 }
471 }
472
473 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700474 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700476 if (host != null) {
477 host.callbacks = null;
478 pruneHostLocked(host);
479 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 }
481 }
482
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700483 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700485 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 return true;
487 }
488 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700489 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 return true;
491 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700492 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700494 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 return true;
496 }
497 // Nobody else can access it.
498 return false;
499 }
500
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700501 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700503 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700505 AppWidgetId id = mAppWidgetIds.get(i);
506 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 return id;
508 }
509 }
510 return null;
511 }
512
513 Provider lookupProviderLocked(ComponentName provider) {
514 final int N = mInstalledProviders.size();
515 for (int i=0; i<N; i++) {
516 Provider p = mInstalledProviders.get(i);
517 if (p.info.provider.equals(provider)) {
518 return p;
519 }
520 }
521 return null;
522 }
523
524 Host lookupHostLocked(int uid, int hostId) {
525 final int N = mHosts.size();
526 for (int i=0; i<N; i++) {
527 Host h = mHosts.get(i);
528 if (h.uid == uid && h.hostId == hostId) {
529 return h;
530 }
531 }
532 return null;
533 }
534
535 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
536 final int N = mHosts.size();
537 for (int i=0; i<N; i++) {
538 Host h = mHosts.get(i);
539 if (h.hostId == hostId && h.packageName.equals(packageName)) {
540 return h;
541 }
542 }
543 Host host = new Host();
544 host.packageName = packageName;
545 host.uid = uid;
546 host.hostId = hostId;
547 mHosts.add(host);
548 return host;
549 }
550
551 void pruneHostLocked(Host host) {
552 if (host.instances.size() == 0 && host.callbacks == null) {
553 mHosts.remove(host);
554 }
555 }
556
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700557 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 PackageManager pm = mPackageManager;
559
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700560 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
562 PackageManager.GET_META_DATA);
563
564 final int N = broadcastReceivers.size();
565 for (int i=0; i<N; i++) {
566 ResolveInfo ri = broadcastReceivers.get(i);
567 addProviderLocked(ri);
568 }
569 }
570
571 boolean addProviderLocked(ResolveInfo ri) {
572 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
573 ri.activityInfo.name), ri);
574 if (p != null) {
575 mInstalledProviders.add(p);
576 return true;
577 } else {
578 return false;
579 }
580 }
581
582 void removeProviderLocked(int index, Provider p) {
583 int N = p.instances.size();
584 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700585 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700587 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 // Stop telling the host about updates for this from now on
589 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700590 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700592 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 id.provider = null;
594 pruneHostLocked(id.host);
595 id.host = null;
596 }
597 p.instances.clear();
598 mInstalledProviders.remove(index);
599 // no need to send the DISABLE broadcast, since the receiver is gone anyway
600 cancelBroadcasts(p);
601 }
602
603 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700604 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 intent.setComponent(p.info.provider);
606 mContext.sendBroadcast(intent);
607 }
608
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700609 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
610 if (appWidgetIds != null && appWidgetIds.length > 0) {
611 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
612 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 intent.setComponent(p.info.provider);
614 mContext.sendBroadcast(intent);
615 }
616 }
617
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700618 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 if (p.info.updatePeriodMillis > 0) {
620 // if this is the first instance, set the alarm. otherwise,
621 // rely on the fact that we've already set it and that
622 // PendingIntent.getBroadcast will update the extras.
623 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700624 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
625 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 intent.setComponent(p.info.provider);
627 long token = Binder.clearCallingIdentity();
628 try {
629 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
630 PendingIntent.FLAG_UPDATE_CURRENT);
631 } finally {
632 Binder.restoreCallingIdentity(token);
633 }
634 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700635 long period = p.info.updatePeriodMillis;
636 if (period < MIN_UPDATE_PERIOD) {
637 period = MIN_UPDATE_PERIOD;
638 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700640 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 }
642 }
643 }
644
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700645 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700647 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700649 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700651 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 }
653
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700654 public int[] getAppWidgetIds(ComponentName provider) {
655 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 Provider p = lookupProviderLocked(provider);
657 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700658 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 } else {
660 return new int[0];
661 }
662 }
663 }
664
665 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
666 Provider p = null;
667
668 ActivityInfo activityInfo = ri.activityInfo;
669 XmlResourceParser parser = null;
670 try {
671 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700672 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 if (parser == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700674 Log.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
675 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 return null;
677 }
678
679 AttributeSet attrs = Xml.asAttributeSet(parser);
680
681 int type;
682 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
683 && type != XmlPullParser.START_TAG) {
684 // drain whitespace, comments, etc.
685 }
686
687 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700688 if (!"appwidget-provider".equals(nodeName)) {
689 Log.w(TAG, "Meta-data does not start with appwidget-provider tag for"
690 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 return null;
692 }
693
694 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700695 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696
697 info.provider = component;
698 p.uid = activityInfo.applicationInfo.uid;
699
700 TypedArray sa = mContext.getResources().obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700701 com.android.internal.R.styleable.AppWidgetProviderInfo);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700702
703 // These dimensions has to be resolved in the application's context.
704 // We simply send back the raw complex data, which will be
705 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
706 TypedValue value = sa.peekValue(
707 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
708 info.minWidth = value != null ? value.data : 0;
709 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
710 info.minHeight = value != null ? value.data : 0;
711
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700713 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700715 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700717 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 if (className != null) {
719 info.configure = new ComponentName(component.getPackageName(), className);
720 }
721 info.label = activityInfo.loadLabel(mPackageManager).toString();
722 info.icon = ri.getIconResource();
723 sa.recycle();
724 } catch (Exception e) {
725 // Ok to catch Exception here, because anything going wrong because
726 // of what a client process passes to us should not be fatal for the
727 // system process.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700728 Log.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 return null;
730 } finally {
731 if (parser != null) parser.close();
732 }
733 return p;
734 }
735
736 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
737 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
738 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
739 throw new PackageManager.NameNotFoundException();
740 }
741 return pkgInfo.applicationInfo.uid;
742 }
743
744 int enforceCallingUid(String packageName) throws IllegalArgumentException {
745 int callingUid = getCallingUid();
746 int packageUid;
747 try {
748 packageUid = getUidForPackage(packageName);
749 } catch (PackageManager.NameNotFoundException ex) {
750 throw new IllegalArgumentException("packageName and uid don't match packageName="
751 + packageName);
752 }
Marco Nelissen54796e72009-04-30 15:16:30 -0700753 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 throw new IllegalArgumentException("packageName and uid don't match packageName="
755 + packageName);
756 }
757 return callingUid;
758 }
759
760 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700761 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 final int N = mInstalledProviders.size();
763 for (int i=0; i<N; i++) {
764 Provider p = mInstalledProviders.get(i);
765 if (p.instances.size() > 0) {
766 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700767 int[] appWidgetIds = getAppWidgetIds(p);
768 sendUpdateIntentLocked(p, appWidgetIds);
769 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 }
771 }
772 }
773 }
774
775 // only call from initialization -- it assumes that the data structures are all empty
776 void loadStateLocked() {
777 File temp = savedStateTempFile();
778 File real = savedStateRealFile();
779
780 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
781 // real one. if there is both a real file and a temp one, assume that the temp one isn't
782 // fully written and delete it.
783 if (real.exists()) {
784 readStateFromFileLocked(real);
785 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700786 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 temp.delete();
788 }
789 } else if (temp.exists()) {
790 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -0700791 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 temp.renameTo(real);
793 }
794 }
795
796 void saveStateLocked() {
797 File temp = savedStateTempFile();
798 File real = savedStateRealFile();
799
800 if (!real.exists()) {
801 // If the real one doesn't exist, it's either because this is the first time
802 // or because something went wrong while copying them. In this case, we can't
803 // trust anything that's in temp. In order to have the loadState code not
804 // use the temporary one until it's fully written, create an empty file
805 // for real, which will we'll shortly delete.
806 try {
Romain Guya5475592009-07-01 17:20:08 -0700807 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 real.createNewFile();
809 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -0700810 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 }
812 }
813
814 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700815 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 temp.delete();
817 }
818
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700819 if (!writeStateToFileLocked(temp)) {
820 Log.w(TAG, "Failed to persist new settings");
821 return;
822 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823
Romain Guya5475592009-07-01 17:20:08 -0700824 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 real.delete();
Romain Guya5475592009-07-01 17:20:08 -0700826 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 temp.renameTo(real);
828 }
829
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700830 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 FileOutputStream stream = null;
832 int N;
833
834 try {
835 stream = new FileOutputStream(file, false);
836 XmlSerializer out = new FastXmlSerializer();
837 out.setOutput(stream, "utf-8");
838 out.startDocument(null, true);
839
840
841 out.startTag(null, "gs");
842
843 int providerIndex = 0;
844 N = mInstalledProviders.size();
845 for (int i=0; i<N; i++) {
846 Provider p = mInstalledProviders.get(i);
847 if (p.instances.size() > 0) {
848 out.startTag(null, "p");
849 out.attribute(null, "pkg", p.info.provider.getPackageName());
850 out.attribute(null, "cl", p.info.provider.getClassName());
851 out.endTag(null, "h");
852 p.tag = providerIndex;
853 providerIndex++;
854 }
855 }
856
857 N = mHosts.size();
858 for (int i=0; i<N; i++) {
859 Host host = mHosts.get(i);
860 out.startTag(null, "h");
861 out.attribute(null, "pkg", host.packageName);
862 out.attribute(null, "id", Integer.toHexString(host.hostId));
863 out.endTag(null, "h");
864 host.tag = i;
865 }
866
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700867 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700869 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700871 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 out.attribute(null, "h", Integer.toHexString(id.host.tag));
873 if (id.provider != null) {
874 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
875 }
876 out.endTag(null, "g");
877 }
878
879 out.endTag(null, "gs");
880
881 out.endDocument();
882 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700883 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 } catch (IOException e) {
885 try {
886 if (stream != null) {
887 stream.close();
888 }
889 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -0700890 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 }
892 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -0700893 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 file.delete();
895 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -0700896 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 }
898 }
899
900 void readStateFromFileLocked(File file) {
901 FileInputStream stream = null;
902
903 boolean success = false;
904
905 try {
906 stream = new FileInputStream(file);
907 XmlPullParser parser = Xml.newPullParser();
908 parser.setInput(stream, null);
909
910 int type;
911 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -0700912 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 do {
914 type = parser.next();
915 if (type == XmlPullParser.START_TAG) {
916 String tag = parser.getName();
917 if ("p".equals(tag)) {
918 // TODO: do we need to check that this package has the same signature
919 // as before?
920 String pkg = parser.getAttributeValue(null, "pkg");
921 String cl = parser.getAttributeValue(null, "cl");
922 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
923 if (p == null && mSafeMode) {
924 // if we're in safe mode, make a temporary one
925 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700926 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 p.info.provider = new ComponentName(pkg, cl);
928 p.zombie = true;
929 mInstalledProviders.add(p);
930 }
931 if (p != null) {
932 // if it wasn't uninstalled or something
933 loadedProviders.put(providerIndex, p);
934 }
935 providerIndex++;
936 }
937 else if ("h".equals(tag)) {
938 Host host = new Host();
939
940 // TODO: do we need to check that this package has the same signature
941 // as before?
942 host.packageName = parser.getAttributeValue(null, "pkg");
943 try {
944 host.uid = getUidForPackage(host.packageName);
945 } catch (PackageManager.NameNotFoundException ex) {
946 host.zombie = true;
947 }
948 if (!host.zombie || mSafeMode) {
949 // In safe mode, we don't discard the hosts we don't recognize
950 // so that they're not pruned from our list. Otherwise, we do.
951 host.hostId = Integer.parseInt(
952 parser.getAttributeValue(null, "id"), 16);
953 mHosts.add(host);
954 }
955 }
956 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700957 AppWidgetId id = new AppWidgetId();
958 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
959 if (id.appWidgetId >= mNextAppWidgetId) {
960 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 }
962
963 String providerString = parser.getAttributeValue(null, "p");
964 if (providerString != null) {
965 // there's no provider if it hasn't been bound yet.
966 // maybe we don't have to save this, but it brings the system
967 // to the state it was in.
968 int pIndex = Integer.parseInt(providerString, 16);
969 id.provider = loadedProviders.get(pIndex);
970 if (false) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700971 Log.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 + pIndex + " which is " + id.provider);
973 }
974 if (id.provider == null) {
975 // This provider is gone. We just let the host figure out
976 // that this happened when it fails to load it.
977 continue;
978 }
979 }
980
981 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
982 id.host = mHosts.get(hIndex);
983 if (id.host == null) {
984 // This host is gone.
985 continue;
986 }
987
988 if (id.provider != null) {
989 id.provider.instances.add(id);
990 }
991 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700992 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 }
994 }
995 } while (type != XmlPullParser.END_DOCUMENT);
996 success = true;
997 } catch (NullPointerException e) {
998 Log.w(TAG, "failed parsing " + file, e);
999 } catch (NumberFormatException e) {
1000 Log.w(TAG, "failed parsing " + file, e);
1001 } catch (XmlPullParserException e) {
1002 Log.w(TAG, "failed parsing " + file, e);
1003 } catch (IOException e) {
1004 Log.w(TAG, "failed parsing " + file, e);
1005 } catch (IndexOutOfBoundsException e) {
1006 Log.w(TAG, "failed parsing " + file, e);
1007 }
1008 try {
1009 if (stream != null) {
1010 stream.close();
1011 }
1012 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001013 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 }
1015
1016 if (success) {
1017 // delete any hosts that didn't manage to get connected (should happen)
1018 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001019 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 pruneHostLocked(mHosts.get(i));
1021 }
1022 } else {
1023 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001024 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 mHosts.clear();
1026 final int N = mInstalledProviders.size();
1027 for (int i=0; i<N; i++) {
1028 mInstalledProviders.get(i).instances.clear();
1029 }
1030 }
1031 }
1032
1033 File savedStateTempFile() {
1034 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1035 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1036 }
1037
1038 File savedStateRealFile() {
1039 return new File("/data/system/" + SETTINGS_FILENAME);
1040 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1041 }
1042
1043 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1044 public void onReceive(Context context, Intent intent) {
1045 String action = intent.getAction();
1046 //Log.d(TAG, "received " + action);
1047 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1048 sendInitialBroadcasts();
1049 } else {
1050 Uri uri = intent.getData();
1051 if (uri == null) {
1052 return;
1053 }
1054 String pkgName = uri.getSchemeSpecificPart();
1055 if (pkgName == null) {
1056 return;
1057 }
1058
1059 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001060 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 Bundle extras = intent.getExtras();
1062 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1063 // The package was just upgraded
1064 updateProvidersForPackageLocked(pkgName);
1065 } else {
1066 // The package was just added
1067 addProvidersForPackageLocked(pkgName);
1068 }
1069 saveStateLocked();
1070 }
1071 }
1072 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
1073 Bundle extras = intent.getExtras();
1074 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1075 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1076 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001077 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 removeProvidersForPackageLocked(pkgName);
1079 saveStateLocked();
1080 }
1081 }
1082 }
1083 }
1084 }
1085 };
1086
1087 // TODO: If there's a better way of matching an intent filter against the
1088 // packages for a given package, use that.
1089 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001090 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1092 PackageManager.GET_META_DATA);
1093
1094 final int N = broadcastReceivers.size();
1095 for (int i=0; i<N; i++) {
1096 ResolveInfo ri = broadcastReceivers.get(i);
1097 ActivityInfo ai = ri.activityInfo;
1098
1099 if (pkgName.equals(ai.packageName)) {
1100 addProviderLocked(ri);
1101 }
1102 }
1103 }
1104
1105 // TODO: If there's a better way of matching an intent filter against the
1106 // packages for a given package, use that.
1107 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001108 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001109 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1111 PackageManager.GET_META_DATA);
1112
1113 // add the missing ones and collect which ones to keep
1114 int N = broadcastReceivers.size();
1115 for (int i=0; i<N; i++) {
1116 ResolveInfo ri = broadcastReceivers.get(i);
1117 ActivityInfo ai = ri.activityInfo;
1118 if (pkgName.equals(ai.packageName)) {
1119 ComponentName component = new ComponentName(ai.packageName, ai.name);
1120 Provider p = lookupProviderLocked(component);
1121 if (p == null) {
1122 if (addProviderLocked(ri)) {
1123 keep.add(ai.name);
1124 }
1125 } else {
1126 Provider parsed = parseProviderInfoXml(component, ri);
1127 if (parsed != null) {
1128 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001129 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 p.info = parsed.info;
1131 // If it's enabled
1132 final int M = p.instances.size();
1133 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001134 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 // Reschedule for the new updatePeriodMillis (don't worry about handling
1136 // it specially if updatePeriodMillis didn't change because we just sent
1137 // an update, and the next one will be updatePeriodMillis from now).
1138 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001139 registerForBroadcastsLocked(p, appWidgetIds);
1140 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001142 AppWidgetId id = p.instances.get(j);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 if (id.host != null && id.host.callbacks != null) {
1144 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001145 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 } catch (RemoteException ex) {
1147 // It failed; remove the callback. No need to prune because
1148 // we know that this host is still referenced by this
1149 // instance.
1150 id.host.callbacks = null;
1151 }
1152 }
1153 }
1154 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001155 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001156 }
1157 }
1158 }
1159 }
1160 }
1161
1162 // prune the ones we don't want to keep
1163 N = mInstalledProviders.size();
1164 for (int i=N-1; i>=0; i--) {
1165 Provider p = mInstalledProviders.get(i);
1166 if (pkgName.equals(p.info.provider.getPackageName())
1167 && !keep.contains(p.info.provider.getClassName())) {
1168 removeProviderLocked(i, p);
1169 }
1170 }
1171 }
1172
1173 void removeProvidersForPackageLocked(String pkgName) {
1174 int N = mInstalledProviders.size();
1175 for (int i=N-1; i>=0; i--) {
1176 Provider p = mInstalledProviders.get(i);
1177 if (pkgName.equals(p.info.provider.getPackageName())) {
1178 removeProviderLocked(i, p);
1179 }
1180 }
1181
1182 // Delete the hosts for this package too
1183 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001184 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 // so we don't need to worry about sending DISABLE broadcasts to them.
1186 N = mHosts.size();
1187 for (int i=N-1; i>=0; i--) {
1188 Host host = mHosts.get(i);
1189 if (pkgName.equals(host.packageName)) {
1190 deleteHostLocked(host);
1191 }
1192 }
1193 }
1194}
1195