blob: ad256571afc7204e6d58a2d7b3e3d27cadf274a4 [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
Winson Chung81f39eb2011-01-11 18:05:01 -080019import java.io.File;
20import java.io.FileDescriptor;
21import java.io.FileInputStream;
22import java.io.FileOutputStream;
23import java.io.IOException;
24import java.io.PrintWriter;
Winson Chung16c8d8a2011-01-20 16:19:33 -080025import java.lang.ref.WeakReference;
Winson Chung81f39eb2011-01-11 18:05:01 -080026import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.Iterator;
30import java.util.List;
31import java.util.Locale;
32
33import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35import org.xmlpull.v1.XmlSerializer;
36
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.app.AlarmManager;
38import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070039import android.appwidget.AppWidgetManager;
40import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.content.BroadcastReceiver;
42import android.content.ComponentName;
43import android.content.Context;
44import android.content.Intent;
Winson Chung81f39eb2011-01-11 18:05:01 -080045import android.content.Intent.FilterComparison;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.content.IntentFilter;
Winson Chung81f39eb2011-01-11 18:05:01 -080047import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.content.pm.ActivityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -040049import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.content.pm.PackageInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080051import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.content.pm.ResolveInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080053import android.content.pm.ServiceInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080054import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.content.res.TypedArray;
56import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.net.Uri;
58import android.os.Binder;
59import android.os.Bundle;
Winson Chung81f39eb2011-01-11 18:05:01 -080060import android.os.Handler;
61import android.os.IBinder;
Marco Nelissen54796e72009-04-30 15:16:30 -070062import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.os.RemoteException;
64import android.os.SystemClock;
65import android.util.AttributeSet;
Winson Chung16c8d8a2011-01-20 16:19:33 -080066import android.util.Log;
Winson Chung81f39eb2011-01-11 18:05:01 -080067import android.util.Pair;
Joe Onorato8a9b2202010-02-26 18:56:32 -080068import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070069import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.util.Xml;
71import android.widget.RemoteViews;
72
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070073import com.android.internal.appwidget.IAppWidgetHost;
Winson Chung81f39eb2011-01-11 18:05:01 -080074import com.android.internal.appwidget.IAppWidgetService;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080075import com.android.internal.util.FastXmlSerializer;
Winson Chung81f39eb2011-01-11 18:05:01 -080076import com.android.internal.widget.IRemoteViewsAdapterConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070078class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070080 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070082 private static final String SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070084 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085
86 /*
87 * When identifying a Host or Provider based on the calling process, use the uid field.
88 * When identifying a Host or Provider based on a package manager broadcast, use the
89 * package given.
90 */
91
92 static class Provider {
93 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070094 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070095 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 PendingIntent broadcast;
97 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
98
99 int tag; // for use while saving state (the index)
100 }
101
102 static class Host {
103 int uid;
104 int hostId;
105 String packageName;
Romain Guya5475592009-07-01 17:20:08 -0700106 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700107 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
109
110 int tag; // for use while saving state (the index)
111 }
112
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700113 static class AppWidgetId {
114 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 Provider provider;
116 RemoteViews views;
117 Host host;
118 }
119
Winson Chung81f39eb2011-01-11 18:05:01 -0800120 /**
121 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
122 * This needs to be a static inner class since a reference to the ServiceConnection is held
123 * globally and may lead us to leak AppWidgetService instances (if there were more than one).
124 */
125 static class ServiceConnectionProxy implements ServiceConnection {
Winson Chung81f39eb2011-01-11 18:05:01 -0800126 private final Pair<Integer, Intent.FilterComparison> mKey;
127 private final IBinder mConnectionCb;
128
Winson Chung16c8d8a2011-01-20 16:19:33 -0800129 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800130 mKey = key;
131 mConnectionCb = connectionCb;
132 }
133 public void onServiceConnected(ComponentName name, IBinder service) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800134 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800135 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
136 try {
137 cb.onServiceConnected(service);
138 } catch (RemoteException e) {
139 e.printStackTrace();
140 }
141 }
142 public void onServiceDisconnected(ComponentName name) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800143 disconnect();
144 }
145 public void disconnect() {
146 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800147 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
148 try {
149 cb.onServiceDisconnected();
Winson Chung81f39eb2011-01-11 18:05:01 -0800150 } catch (RemoteException e) {
151 e.printStackTrace();
152 }
153 }
154 }
155
156 // Manages connections to RemoteViewsServices
157 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
158 mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
Winson Chung81f39eb2011-01-11 18:05:01 -0800159
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700161 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 PackageManager mPackageManager;
163 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700164 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700165 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700166 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
167 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 boolean mSafeMode;
169
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700170 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 mContext = context;
172 mPackageManager = context.getPackageManager();
173 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
174 }
175
176 public void systemReady(boolean safeMode) {
177 mSafeMode = safeMode;
178
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700179 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 loadStateLocked();
181
182 // Register for the boot completed broadcast, so we can send the
183 // ENABLE broacasts. If we try to send them now, they time out,
184 // because the system isn't ready to handle them yet.
185 mContext.registerReceiver(mBroadcastReceiver,
186 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
187
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700188 // Register for configuration changes so we can update the names
189 // of the widgets when the locale changes.
190 mContext.registerReceiver(mBroadcastReceiver,
191 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 // Register for broadcasts about package install, etc., so we can
194 // update the provider list.
195 IntentFilter filter = new IntentFilter();
196 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
Joe Onoratod070e892011-01-07 20:50:37 -0800197 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
199 filter.addDataScheme("package");
200 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800201 // Register for events related to sdcard installation.
202 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800203 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
204 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800205 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
207
208 @Override
209 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
210 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
211 != PackageManager.PERMISSION_GRANTED) {
212 pw.println("Permission Denial: can't dump from from pid="
213 + Binder.getCallingPid()
214 + ", uid=" + Binder.getCallingUid());
215 return;
216 }
217
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700218 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700220 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 for (int i=0; i<N; i++) {
222 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700223 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700224 pw.print(" ["); pw.print(i); pw.print("] provider ");
225 pw.print(info.provider.flattenToShortString());
226 pw.println(':');
227 pw.print(" min=("); pw.print(info.minWidth);
228 pw.print("x"); pw.print(info.minHeight);
229 pw.print(") updatePeriodMillis=");
230 pw.print(info.updatePeriodMillis);
231 pw.print(" initialLayout=#");
232 pw.print(Integer.toHexString(info.initialLayout));
233 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 }
235
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700236 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700237 pw.println(" ");
238 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700240 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700241 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700242 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700243 pw.print(" hostId=");
244 pw.print(id.host.hostId); pw.print(' ');
245 pw.print(id.host.packageName); pw.print('/');
246 pw.println(id.host.uid);
247 if (id.provider != null) {
248 pw.print(" provider=");
249 pw.println(id.provider.info.provider.flattenToShortString());
250 }
251 if (id.host != null) {
252 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
253 }
254 if (id.views != null) {
255 pw.print(" views="); pw.println(id.views);
256 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 }
258
259 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700260 pw.println(" ");
261 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 for (int i=0; i<N; i++) {
263 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700264 pw.print(" ["); pw.print(i); pw.print("] hostId=");
265 pw.print(host.hostId); pw.print(' ');
266 pw.print(host.packageName); pw.print('/');
267 pw.print(host.uid); pw.println(':');
268 pw.print(" callbacks="); pw.println(host.callbacks);
269 pw.print(" instances.size="); pw.print(host.instances.size());
270 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 }
272 }
273 }
274
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700275 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700277 synchronized (mAppWidgetIds) {
278 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279
280 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
281
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700282 AppWidgetId id = new AppWidgetId();
283 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 id.host = host;
285
286 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700287 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288
289 saveStateLocked();
290
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700291 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 }
293 }
294
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700295 public void deleteAppWidgetId(int appWidgetId) {
296 synchronized (mAppWidgetIds) {
297 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700299 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 saveStateLocked();
301 }
302 }
303 }
304
305 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700306 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 int callingUid = getCallingUid();
308 Host host = lookupHostLocked(callingUid, hostId);
309 if (host != null) {
310 deleteHostLocked(host);
311 saveStateLocked();
312 }
313 }
314 }
315
316 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700317 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 int callingUid = getCallingUid();
319 final int N = mHosts.size();
320 boolean changed = false;
321 for (int i=N-1; i>=0; i--) {
322 Host host = mHosts.get(i);
323 if (host.uid == callingUid) {
324 deleteHostLocked(host);
325 changed = true;
326 }
327 }
328 if (changed) {
329 saveStateLocked();
330 }
331 }
332 }
333
334 void deleteHostLocked(Host host) {
335 final int N = host.instances.size();
336 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700337 AppWidgetId id = host.instances.get(i);
338 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 }
340 host.instances.clear();
341 mHosts.remove(host);
342 // it's gone or going away, abruptly drop the callback connection
343 host.callbacks = null;
344 }
345
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700346 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800347 // We first unbind all services that are bound to this id
348 unbindAppWidgetRemoteViewsServicesLocked(id);
349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 Host host = id.host;
351 host.instances.remove(id);
352 pruneHostLocked(host);
353
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700354 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355
356 Provider p = id.provider;
357 if (p != null) {
358 p.instances.remove(id);
359 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700360 // send the broacast saying that this appWidgetId has been deleted
361 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700363 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 mContext.sendBroadcast(intent);
365 if (p.instances.size() == 0) {
366 // cancel the future updates
367 cancelBroadcasts(p);
368
369 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700370 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 intent.setComponent(p.info.provider);
372 mContext.sendBroadcast(intent);
373 }
374 }
375 }
376 }
377
378 void cancelBroadcasts(Provider p) {
379 if (p.broadcast != null) {
380 mAlarmManager.cancel(p.broadcast);
381 long token = Binder.clearCallingIdentity();
382 try {
383 p.broadcast.cancel();
384 } finally {
385 Binder.restoreCallingIdentity(token);
386 }
387 p.broadcast = null;
388 }
389 }
390
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700391 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
392 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
393 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
394 synchronized (mAppWidgetIds) {
395 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700397 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 }
399 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700400 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 + id.provider.info.provider);
402 }
403 Provider p = lookupProviderLocked(provider);
404 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700405 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 }
407 if (p.zombie) {
408 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
409 + " safe mode: " + provider);
410 }
411
412 id.provider = p;
413 p.instances.add(id);
414 int instancesSize = p.instances.size();
415 if (instancesSize == 1) {
416 // tell the provider that it's ready
417 sendEnableIntentLocked(p);
418 }
419
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700420 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 // It's less critical when the next one happens, so when we schdule the next one,
422 // we add updatePeriodMillis to its start time. That time will have some slop,
423 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700424 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425
426 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700427 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 saveStateLocked();
429 }
430 }
431
Winson Chung81f39eb2011-01-11 18:05:01 -0800432 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
433 synchronized (mAppWidgetIds) {
434 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
435 if (id == null) {
436 throw new IllegalArgumentException("bad appWidgetId");
437 }
438 final ComponentName componentName = intent.getComponent();
439 try {
440 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
441 PackageManager.GET_PERMISSIONS);
442 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
443 throw new SecurityException("Selected service does not require "
444 + android.Manifest.permission.BIND_REMOTEVIEWS
445 + ": " + componentName);
446 }
447 } catch (PackageManager.NameNotFoundException e) {
448 throw new IllegalArgumentException("Unknown component " + componentName);
449 }
450
Winson Chung16c8d8a2011-01-20 16:19:33 -0800451 // If there is already a connection made for this service intent, then disconnect from
452 // that first. (This does not allow multiple connections to the same service under
453 // the same key)
454 ServiceConnectionProxy conn = null;
Winson Chung81f39eb2011-01-11 18:05:01 -0800455 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
456 new FilterComparison(intent));
Winson Chung16c8d8a2011-01-20 16:19:33 -0800457 if (mBoundRemoteViewsServices.containsKey(key)) {
458 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
459 conn.disconnect();
460 mContext.unbindService(conn);
461 mBoundRemoteViewsServices.remove(key);
462 }
463
464 // Bind to the RemoteViewsService (which will trigger a callback to the
465 // RemoteViewsAdapter.onServiceConnected())
Winson Chung81f39eb2011-01-11 18:05:01 -0800466 final long token = Binder.clearCallingIdentity();
467 try {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800468 conn = new ServiceConnectionProxy(key, connection);
Winson Chung81f39eb2011-01-11 18:05:01 -0800469 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
470 mBoundRemoteViewsServices.put(key, conn);
471 } finally {
472 Binder.restoreCallingIdentity(token);
473 }
474 }
475 }
476
477 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
478 synchronized (mAppWidgetIds) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800479 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
480 // RemoteViewsAdapter)
481 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
482 new FilterComparison(intent));
483 if (mBoundRemoteViewsServices.containsKey(key)) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800484 // We don't need to use the appWidgetId until after we are sure there is something
485 // to unbind. Note that this may mask certain issues with apps calling unbind()
486 // more than necessary.
487 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
488 if (id == null) {
489 throw new IllegalArgumentException("bad appWidgetId");
490 }
491
492 ServiceConnectionProxy conn =
493 (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
494 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800495 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800496 mBoundRemoteViewsServices.remove(key);
497 } else {
498 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
Winson Chung81f39eb2011-01-11 18:05:01 -0800499 }
500 }
501 }
502
503 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800504 int appWidgetId = id.appWidgetId;
505 // Unbind all connections to Services bound to this AppWidgetId
Winson Chung81f39eb2011-01-11 18:05:01 -0800506 Iterator<Pair<Integer, Intent.FilterComparison>> it =
507 mBoundRemoteViewsServices.keySet().iterator();
Winson Chung81f39eb2011-01-11 18:05:01 -0800508 while (it.hasNext()) {
509 final Pair<Integer, Intent.FilterComparison> key = it.next();
510 if (key.first.intValue() == appWidgetId) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800511 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
512 mBoundRemoteViewsServices.get(key);
513 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800514 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800515 it.remove();
Winson Chung81f39eb2011-01-11 18:05:01 -0800516 }
517 }
518 }
519
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700520 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
521 synchronized (mAppWidgetIds) {
522 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 if (id != null && id.provider != null && !id.provider.zombie) {
524 return id.provider.info;
525 }
526 return null;
527 }
528 }
529
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700530 public RemoteViews getAppWidgetViews(int appWidgetId) {
531 synchronized (mAppWidgetIds) {
532 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 if (id != null) {
534 return id.views;
535 }
536 return null;
537 }
538 }
539
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700540 public List<AppWidgetProviderInfo> getInstalledProviders() {
541 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700543 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 for (int i=0; i<N; i++) {
545 Provider p = mInstalledProviders.get(i);
546 if (!p.zombie) {
547 result.add(p.info);
548 }
549 }
550 return result;
551 }
552 }
553
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700554 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
555 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 return;
557 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700558 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 return;
560 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700561 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700563 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700565 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
566 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 }
568 }
569 }
570
Adam Cohen2dd21972010-08-15 18:20:04 -0700571 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
572 if (appWidgetIds == null) {
573 return;
574 }
575 if (appWidgetIds.length == 0) {
576 return;
577 }
578 final int N = appWidgetIds.length;
579
580 synchronized (mAppWidgetIds) {
581 for (int i=0; i<N; i++) {
582 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
583 updateAppWidgetInstanceLocked(id, views, true);
584 }
585 }
586 }
587
Winson Chung6394c0e2010-08-16 10:14:56 -0700588 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700589 if (appWidgetIds == null) {
590 return;
591 }
592 if (appWidgetIds.length == 0) {
593 return;
594 }
595 final int N = appWidgetIds.length;
596
597 synchronized (mAppWidgetIds) {
598 for (int i=0; i<N; i++) {
599 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700600 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700601 }
602 }
603 }
604
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700605 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
606 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 Provider p = lookupProviderLocked(provider);
608 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800609 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 return;
611 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700612 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 final int N = instances.size();
614 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700615 AppWidgetId id = instances.get(i);
616 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 }
618 }
619 }
620
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700621 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700622 updateAppWidgetInstanceLocked(id, views, false);
623 }
624
625 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700626 // allow for stale appWidgetIds and other badness
627 // lookup also checks that the calling process can access the appWidgetId
628 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700630
631 // We do not want to save this RemoteViews
632 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633
634 // is anyone listening?
635 if (id.host.callbacks != null) {
636 try {
637 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700638 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 } catch (RemoteException e) {
640 // It failed; remove the callback. No need to prune because
641 // we know that this host is still referenced by this instance.
642 id.host.callbacks = null;
643 }
644 }
645 }
646 }
647
Winson Chung6394c0e2010-08-16 10:14:56 -0700648 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700649 // allow for stale appWidgetIds and other badness
650 // lookup also checks that the calling process can access the appWidgetId
651 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
652 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700653 // is anyone listening?
654 if (id.host.callbacks != null) {
655 try {
656 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700657 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700658 } catch (RemoteException e) {
659 // It failed; remove the callback. No need to prune because
660 // we know that this host is still referenced by this instance.
661 id.host.callbacks = null;
662 }
663 }
664 }
665 }
666
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700667 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 List<RemoteViews> updatedViews) {
669 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700670 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
672 host.callbacks = callbacks;
673
674 updatedViews.clear();
675
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700676 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 int N = instances.size();
678 int[] updatedIds = new int[N];
679 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700680 AppWidgetId id = instances.get(i);
681 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 updatedViews.add(id.views);
683 }
684 return updatedIds;
685 }
686 }
687
688 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700689 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700691 if (host != null) {
692 host.callbacks = null;
693 pruneHostLocked(host);
694 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 }
696 }
697
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700698 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700700 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 return true;
702 }
703 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700704 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 return true;
706 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700707 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700709 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 return true;
711 }
712 // Nobody else can access it.
713 return false;
714 }
715
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700716 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700718 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700720 AppWidgetId id = mAppWidgetIds.get(i);
721 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 return id;
723 }
724 }
725 return null;
726 }
727
728 Provider lookupProviderLocked(ComponentName provider) {
Romain Guyd2671e12010-03-11 18:06:42 -0800729 final String className = provider.getClassName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 final int N = mInstalledProviders.size();
731 for (int i=0; i<N; i++) {
732 Provider p = mInstalledProviders.get(i);
Romain Guyd2671e12010-03-11 18:06:42 -0800733 if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 return p;
735 }
736 }
737 return null;
738 }
739
740 Host lookupHostLocked(int uid, int hostId) {
741 final int N = mHosts.size();
742 for (int i=0; i<N; i++) {
743 Host h = mHosts.get(i);
744 if (h.uid == uid && h.hostId == hostId) {
745 return h;
746 }
747 }
748 return null;
749 }
750
751 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
752 final int N = mHosts.size();
753 for (int i=0; i<N; i++) {
754 Host h = mHosts.get(i);
755 if (h.hostId == hostId && h.packageName.equals(packageName)) {
756 return h;
757 }
758 }
759 Host host = new Host();
760 host.packageName = packageName;
761 host.uid = uid;
762 host.hostId = hostId;
763 mHosts.add(host);
764 return host;
765 }
766
767 void pruneHostLocked(Host host) {
768 if (host.instances.size() == 0 && host.callbacks == null) {
769 mHosts.remove(host);
770 }
771 }
772
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700773 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 PackageManager pm = mPackageManager;
775
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700776 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
778 PackageManager.GET_META_DATA);
779
Bjorn Bringert5f857802010-02-10 23:09:48 +0000780 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 for (int i=0; i<N; i++) {
782 ResolveInfo ri = broadcastReceivers.get(i);
783 addProviderLocked(ri);
784 }
785 }
786
787 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800788 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
789 return false;
790 }
791 if (!ri.activityInfo.isEnabled()) {
792 return false;
793 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
795 ri.activityInfo.name), ri);
796 if (p != null) {
797 mInstalledProviders.add(p);
798 return true;
799 } else {
800 return false;
801 }
802 }
803
804 void removeProviderLocked(int index, Provider p) {
805 int N = p.instances.size();
806 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700807 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700809 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 // Stop telling the host about updates for this from now on
811 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700812 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700814 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 id.provider = null;
816 pruneHostLocked(id.host);
817 id.host = null;
818 }
819 p.instances.clear();
820 mInstalledProviders.remove(index);
821 // no need to send the DISABLE broadcast, since the receiver is gone anyway
822 cancelBroadcasts(p);
823 }
824
825 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700826 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 intent.setComponent(p.info.provider);
828 mContext.sendBroadcast(intent);
829 }
830
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700831 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
832 if (appWidgetIds != null && appWidgetIds.length > 0) {
833 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
834 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835 intent.setComponent(p.info.provider);
836 mContext.sendBroadcast(intent);
837 }
838 }
839
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700840 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 if (p.info.updatePeriodMillis > 0) {
842 // if this is the first instance, set the alarm. otherwise,
843 // rely on the fact that we've already set it and that
844 // PendingIntent.getBroadcast will update the extras.
845 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700846 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
847 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 intent.setComponent(p.info.provider);
849 long token = Binder.clearCallingIdentity();
850 try {
851 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
852 PendingIntent.FLAG_UPDATE_CURRENT);
853 } finally {
854 Binder.restoreCallingIdentity(token);
855 }
856 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700857 long period = p.info.updatePeriodMillis;
858 if (period < MIN_UPDATE_PERIOD) {
859 period = MIN_UPDATE_PERIOD;
860 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700862 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 }
864 }
865 }
866
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700867 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700869 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700871 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700873 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 }
875
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700876 public int[] getAppWidgetIds(ComponentName provider) {
877 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 Provider p = lookupProviderLocked(provider);
879 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700880 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 } else {
882 return new int[0];
883 }
884 }
885 }
886
887 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
888 Provider p = null;
889
890 ActivityInfo activityInfo = ri.activityInfo;
891 XmlResourceParser parser = null;
892 try {
893 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700894 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800896 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700897 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 return null;
899 }
900
901 AttributeSet attrs = Xml.asAttributeSet(parser);
902
903 int type;
904 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
905 && type != XmlPullParser.START_TAG) {
906 // drain whitespace, comments, etc.
907 }
908
909 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700910 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800911 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700912 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 return null;
914 }
915
916 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700917 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
Romain Guyd2671e12010-03-11 18:06:42 -0800918 // If metaData was null, we would have returned earlier when getting
919 // the parser No need to do the check here
920 info.oldName = activityInfo.metaData.getString(
921 AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922
923 info.provider = component;
924 p.uid = activityInfo.applicationInfo.uid;
925
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800926 Resources res = mPackageManager.getResourcesForApplication(
927 activityInfo.applicationInfo);
928
929 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700930 com.android.internal.R.styleable.AppWidgetProviderInfo);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700931
932 // These dimensions has to be resolved in the application's context.
933 // We simply send back the raw complex data, which will be
934 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
935 TypedValue value = sa.peekValue(
936 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
937 info.minWidth = value != null ? value.data : 0;
938 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
939 info.minHeight = value != null ? value.data : 0;
940
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700942 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700944 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700946 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 if (className != null) {
948 info.configure = new ComponentName(component.getPackageName(), className);
949 }
950 info.label = activityInfo.loadLabel(mPackageManager).toString();
951 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -0700952 info.previewImage = sa.getResourceId(
953 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -0700954 info.autoAdvanceViewId = sa.getResourceId(
955 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -0700956
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 sa.recycle();
958 } catch (Exception e) {
959 // Ok to catch Exception here, because anything going wrong because
960 // of what a client process passes to us should not be fatal for the
961 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800962 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 return null;
964 } finally {
965 if (parser != null) parser.close();
966 }
967 return p;
968 }
969
970 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
971 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
972 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
973 throw new PackageManager.NameNotFoundException();
974 }
975 return pkgInfo.applicationInfo.uid;
976 }
977
978 int enforceCallingUid(String packageName) throws IllegalArgumentException {
979 int callingUid = getCallingUid();
980 int packageUid;
981 try {
982 packageUid = getUidForPackage(packageName);
983 } catch (PackageManager.NameNotFoundException ex) {
984 throw new IllegalArgumentException("packageName and uid don't match packageName="
985 + packageName);
986 }
Marco Nelissen54796e72009-04-30 15:16:30 -0700987 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 throw new IllegalArgumentException("packageName and uid don't match packageName="
989 + packageName);
990 }
991 return callingUid;
992 }
993
994 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700995 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 final int N = mInstalledProviders.size();
997 for (int i=0; i<N; i++) {
998 Provider p = mInstalledProviders.get(i);
999 if (p.instances.size() > 0) {
1000 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001001 int[] appWidgetIds = getAppWidgetIds(p);
1002 sendUpdateIntentLocked(p, appWidgetIds);
1003 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 }
1005 }
1006 }
1007 }
1008
1009 // only call from initialization -- it assumes that the data structures are all empty
1010 void loadStateLocked() {
1011 File temp = savedStateTempFile();
1012 File real = savedStateRealFile();
1013
1014 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1015 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1016 // fully written and delete it.
1017 if (real.exists()) {
1018 readStateFromFileLocked(real);
1019 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001020 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 temp.delete();
1022 }
1023 } else if (temp.exists()) {
1024 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001025 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 temp.renameTo(real);
1027 }
1028 }
1029
1030 void saveStateLocked() {
1031 File temp = savedStateTempFile();
1032 File real = savedStateRealFile();
1033
1034 if (!real.exists()) {
1035 // If the real one doesn't exist, it's either because this is the first time
1036 // or because something went wrong while copying them. In this case, we can't
1037 // trust anything that's in temp. In order to have the loadState code not
1038 // use the temporary one until it's fully written, create an empty file
1039 // for real, which will we'll shortly delete.
1040 try {
Romain Guya5475592009-07-01 17:20:08 -07001041 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 real.createNewFile();
1043 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001044 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 }
1046 }
1047
1048 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001049 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 temp.delete();
1051 }
1052
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001053 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001054 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001055 return;
1056 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057
Romain Guya5475592009-07-01 17:20:08 -07001058 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001060 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 temp.renameTo(real);
1062 }
1063
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001064 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 FileOutputStream stream = null;
1066 int N;
1067
1068 try {
1069 stream = new FileOutputStream(file, false);
1070 XmlSerializer out = new FastXmlSerializer();
1071 out.setOutput(stream, "utf-8");
1072 out.startDocument(null, true);
1073
1074
1075 out.startTag(null, "gs");
1076
1077 int providerIndex = 0;
1078 N = mInstalledProviders.size();
1079 for (int i=0; i<N; i++) {
1080 Provider p = mInstalledProviders.get(i);
1081 if (p.instances.size() > 0) {
1082 out.startTag(null, "p");
1083 out.attribute(null, "pkg", p.info.provider.getPackageName());
1084 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001085 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 p.tag = providerIndex;
1087 providerIndex++;
1088 }
1089 }
1090
1091 N = mHosts.size();
1092 for (int i=0; i<N; i++) {
1093 Host host = mHosts.get(i);
1094 out.startTag(null, "h");
1095 out.attribute(null, "pkg", host.packageName);
1096 out.attribute(null, "id", Integer.toHexString(host.hostId));
1097 out.endTag(null, "h");
1098 host.tag = i;
1099 }
1100
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001101 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001103 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001105 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1107 if (id.provider != null) {
1108 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1109 }
1110 out.endTag(null, "g");
1111 }
1112
1113 out.endTag(null, "gs");
1114
1115 out.endDocument();
1116 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001117 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 } catch (IOException e) {
1119 try {
1120 if (stream != null) {
1121 stream.close();
1122 }
1123 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001124 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 }
1126 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001127 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 file.delete();
1129 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001130 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 }
1132 }
1133
1134 void readStateFromFileLocked(File file) {
1135 FileInputStream stream = null;
1136
1137 boolean success = false;
1138
1139 try {
1140 stream = new FileInputStream(file);
1141 XmlPullParser parser = Xml.newPullParser();
1142 parser.setInput(stream, null);
1143
1144 int type;
1145 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001146 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 do {
1148 type = parser.next();
1149 if (type == XmlPullParser.START_TAG) {
1150 String tag = parser.getName();
1151 if ("p".equals(tag)) {
1152 // TODO: do we need to check that this package has the same signature
1153 // as before?
1154 String pkg = parser.getAttributeValue(null, "pkg");
1155 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001156
1157 final PackageManager packageManager = mContext.getPackageManager();
1158 try {
1159 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1160 } catch (PackageManager.NameNotFoundException e) {
1161 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1162 new String[] { pkg });
1163 pkg = pkgs[0];
1164 }
1165
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1167 if (p == null && mSafeMode) {
1168 // if we're in safe mode, make a temporary one
1169 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001170 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 p.info.provider = new ComponentName(pkg, cl);
1172 p.zombie = true;
1173 mInstalledProviders.add(p);
1174 }
1175 if (p != null) {
1176 // if it wasn't uninstalled or something
1177 loadedProviders.put(providerIndex, p);
1178 }
1179 providerIndex++;
1180 }
1181 else if ("h".equals(tag)) {
1182 Host host = new Host();
1183
1184 // TODO: do we need to check that this package has the same signature
1185 // as before?
1186 host.packageName = parser.getAttributeValue(null, "pkg");
1187 try {
1188 host.uid = getUidForPackage(host.packageName);
1189 } catch (PackageManager.NameNotFoundException ex) {
1190 host.zombie = true;
1191 }
1192 if (!host.zombie || mSafeMode) {
1193 // In safe mode, we don't discard the hosts we don't recognize
1194 // so that they're not pruned from our list. Otherwise, we do.
1195 host.hostId = Integer.parseInt(
1196 parser.getAttributeValue(null, "id"), 16);
1197 mHosts.add(host);
1198 }
1199 }
1200 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001201 AppWidgetId id = new AppWidgetId();
1202 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1203 if (id.appWidgetId >= mNextAppWidgetId) {
1204 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001205 }
1206
1207 String providerString = parser.getAttributeValue(null, "p");
1208 if (providerString != null) {
1209 // there's no provider if it hasn't been bound yet.
1210 // maybe we don't have to save this, but it brings the system
1211 // to the state it was in.
1212 int pIndex = Integer.parseInt(providerString, 16);
1213 id.provider = loadedProviders.get(pIndex);
1214 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001215 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 + pIndex + " which is " + id.provider);
1217 }
1218 if (id.provider == null) {
1219 // This provider is gone. We just let the host figure out
1220 // that this happened when it fails to load it.
1221 continue;
1222 }
1223 }
1224
1225 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1226 id.host = mHosts.get(hIndex);
1227 if (id.host == null) {
1228 // This host is gone.
1229 continue;
1230 }
1231
1232 if (id.provider != null) {
1233 id.provider.instances.add(id);
1234 }
1235 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001236 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237 }
1238 }
1239 } while (type != XmlPullParser.END_DOCUMENT);
1240 success = true;
1241 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001242 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001244 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001246 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001248 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001250 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001251 }
1252 try {
1253 if (stream != null) {
1254 stream.close();
1255 }
1256 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001257 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001258 }
1259
1260 if (success) {
1261 // delete any hosts that didn't manage to get connected (should happen)
1262 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001263 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264 pruneHostLocked(mHosts.get(i));
1265 }
1266 } else {
1267 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001268 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 mHosts.clear();
1270 final int N = mInstalledProviders.size();
1271 for (int i=0; i<N; i++) {
1272 mInstalledProviders.get(i).instances.clear();
1273 }
1274 }
1275 }
1276
1277 File savedStateTempFile() {
1278 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1279 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1280 }
1281
1282 File savedStateRealFile() {
1283 return new File("/data/system/" + SETTINGS_FILENAME);
1284 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1285 }
1286
1287 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1288 public void onReceive(Context context, Intent intent) {
1289 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001290 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001291 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1292 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001293 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1294 Locale revised = Locale.getDefault();
1295 if (revised == null || mLocale == null ||
1296 !(revised.equals(mLocale))) {
1297 mLocale = revised;
1298
1299 synchronized (mAppWidgetIds) {
1300 int N = mInstalledProviders.size();
1301 for (int i=N-1; i>=0; i--) {
1302 Provider p = mInstalledProviders.get(i);
1303 String pkgName = p.info.provider.getPackageName();
1304 updateProvidersForPackageLocked(pkgName);
1305 }
1306 saveStateLocked();
1307 }
1308 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001310 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001311 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001312 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001313 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001314 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1315 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001316 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001317 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1318 added = false;
1319 } else {
1320 Uri uri = intent.getData();
1321 if (uri == null) {
1322 return;
1323 }
1324 String pkgName = uri.getSchemeSpecificPart();
1325 if (pkgName == null) {
1326 return;
1327 }
1328 pkgList = new String[] { pkgName };
1329 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001330 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001331 }
1332 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 return;
1334 }
Joe Onoratod070e892011-01-07 20:50:37 -08001335 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001336 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001338 if (changed || (extras != null &&
1339 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001340 for (String pkgName : pkgList) {
1341 // The package was just upgraded
1342 updateProvidersForPackageLocked(pkgName);
1343 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 } else {
1345 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001346 for (String pkgName : pkgList) {
1347 addProvidersForPackageLocked(pkgName);
1348 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 }
1350 saveStateLocked();
1351 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001352 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001353 Bundle extras = intent.getExtras();
1354 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1355 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1356 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001357 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001358 for (String pkgName : pkgList) {
1359 removeProvidersForPackageLocked(pkgName);
1360 saveStateLocked();
1361 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 }
1363 }
1364 }
1365 }
1366 }
1367 };
1368
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001370 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001371 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1373 PackageManager.GET_META_DATA);
1374
Bjorn Bringert5f857802010-02-10 23:09:48 +00001375 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 for (int i=0; i<N; i++) {
1377 ResolveInfo ri = broadcastReceivers.get(i);
1378 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001379 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1380 continue;
1381 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 if (pkgName.equals(ai.packageName)) {
1383 addProviderLocked(ri);
1384 }
1385 }
1386 }
1387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001389 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001390 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001391 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001392 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1393 PackageManager.GET_META_DATA);
1394
1395 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001396 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001397 for (int i=0; i<N; i++) {
1398 ResolveInfo ri = broadcastReceivers.get(i);
1399 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001400 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1401 continue;
1402 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 if (pkgName.equals(ai.packageName)) {
1404 ComponentName component = new ComponentName(ai.packageName, ai.name);
1405 Provider p = lookupProviderLocked(component);
1406 if (p == null) {
1407 if (addProviderLocked(ri)) {
1408 keep.add(ai.name);
1409 }
1410 } else {
1411 Provider parsed = parseProviderInfoXml(component, ri);
1412 if (parsed != null) {
1413 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001414 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001415 p.info = parsed.info;
1416 // If it's enabled
1417 final int M = p.instances.size();
1418 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001419 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001420 // Reschedule for the new updatePeriodMillis (don't worry about handling
1421 // it specially if updatePeriodMillis didn't change because we just sent
1422 // an update, and the next one will be updatePeriodMillis from now).
1423 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001424 registerForBroadcastsLocked(p, appWidgetIds);
1425 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001427 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001428 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 if (id.host != null && id.host.callbacks != null) {
1430 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001431 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 } catch (RemoteException ex) {
1433 // It failed; remove the callback. No need to prune because
1434 // we know that this host is still referenced by this
1435 // instance.
1436 id.host.callbacks = null;
1437 }
1438 }
1439 }
1440 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001441 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 }
1443 }
1444 }
1445 }
1446 }
1447
1448 // prune the ones we don't want to keep
1449 N = mInstalledProviders.size();
1450 for (int i=N-1; i>=0; i--) {
1451 Provider p = mInstalledProviders.get(i);
1452 if (pkgName.equals(p.info.provider.getPackageName())
1453 && !keep.contains(p.info.provider.getClassName())) {
1454 removeProviderLocked(i, p);
1455 }
1456 }
1457 }
1458
1459 void removeProvidersForPackageLocked(String pkgName) {
1460 int N = mInstalledProviders.size();
1461 for (int i=N-1; i>=0; i--) {
1462 Provider p = mInstalledProviders.get(i);
1463 if (pkgName.equals(p.info.provider.getPackageName())) {
1464 removeProviderLocked(i, p);
1465 }
1466 }
1467
1468 // Delete the hosts for this package too
1469 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001470 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001471 // so we don't need to worry about sending DISABLE broadcasts to them.
1472 N = mHosts.size();
1473 for (int i=N-1; i>=0; i--) {
1474 Host host = mHosts.get(i);
1475 if (pkgName.equals(host.packageName)) {
1476 deleteHostLocked(host);
1477 }
1478 }
1479 }
1480}
1481