blob: 550bb3de56a09327ebe08306238c4a9c227ff0d0 [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 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800900
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -0800902
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 int type;
904 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
905 && type != XmlPullParser.START_TAG) {
906 // drain whitespace, comments, etc.
907 }
Adam Cohend2e20de2011-02-25 12:03:37 -0800908
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 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);
Adam Cohend2e20de2011-02-25 12:03:37 -0800928
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800929 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700930 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -0800931
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700932 // 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;
Adam Cohend2e20de2011-02-25 12:03:37 -0800940
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);
Adam Cohend2e20de2011-02-25 12:03:37 -0800956 info.resizableMode = sa.getInt(
957 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
958 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -0700959
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 sa.recycle();
961 } catch (Exception e) {
962 // Ok to catch Exception here, because anything going wrong because
963 // of what a client process passes to us should not be fatal for the
964 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800965 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 return null;
967 } finally {
968 if (parser != null) parser.close();
969 }
970 return p;
971 }
972
973 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
974 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
975 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
976 throw new PackageManager.NameNotFoundException();
977 }
978 return pkgInfo.applicationInfo.uid;
979 }
980
981 int enforceCallingUid(String packageName) throws IllegalArgumentException {
982 int callingUid = getCallingUid();
983 int packageUid;
984 try {
985 packageUid = getUidForPackage(packageName);
986 } catch (PackageManager.NameNotFoundException ex) {
987 throw new IllegalArgumentException("packageName and uid don't match packageName="
988 + packageName);
989 }
Marco Nelissen54796e72009-04-30 15:16:30 -0700990 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800991 throw new IllegalArgumentException("packageName and uid don't match packageName="
992 + packageName);
993 }
994 return callingUid;
995 }
996
997 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700998 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 final int N = mInstalledProviders.size();
1000 for (int i=0; i<N; i++) {
1001 Provider p = mInstalledProviders.get(i);
1002 if (p.instances.size() > 0) {
1003 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001004 int[] appWidgetIds = getAppWidgetIds(p);
1005 sendUpdateIntentLocked(p, appWidgetIds);
1006 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 }
1008 }
1009 }
1010 }
1011
1012 // only call from initialization -- it assumes that the data structures are all empty
1013 void loadStateLocked() {
1014 File temp = savedStateTempFile();
1015 File real = savedStateRealFile();
1016
1017 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1018 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1019 // fully written and delete it.
1020 if (real.exists()) {
1021 readStateFromFileLocked(real);
1022 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001023 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 temp.delete();
1025 }
1026 } else if (temp.exists()) {
1027 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001028 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029 temp.renameTo(real);
1030 }
1031 }
1032
1033 void saveStateLocked() {
1034 File temp = savedStateTempFile();
1035 File real = savedStateRealFile();
1036
1037 if (!real.exists()) {
1038 // If the real one doesn't exist, it's either because this is the first time
1039 // or because something went wrong while copying them. In this case, we can't
1040 // trust anything that's in temp. In order to have the loadState code not
1041 // use the temporary one until it's fully written, create an empty file
1042 // for real, which will we'll shortly delete.
1043 try {
Romain Guya5475592009-07-01 17:20:08 -07001044 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 real.createNewFile();
1046 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001047 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 }
1049 }
1050
1051 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001052 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 temp.delete();
1054 }
1055
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001056 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001057 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001058 return;
1059 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060
Romain Guya5475592009-07-01 17:20:08 -07001061 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001063 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 temp.renameTo(real);
1065 }
1066
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001067 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 FileOutputStream stream = null;
1069 int N;
1070
1071 try {
1072 stream = new FileOutputStream(file, false);
1073 XmlSerializer out = new FastXmlSerializer();
1074 out.setOutput(stream, "utf-8");
1075 out.startDocument(null, true);
1076
1077
1078 out.startTag(null, "gs");
1079
1080 int providerIndex = 0;
1081 N = mInstalledProviders.size();
1082 for (int i=0; i<N; i++) {
1083 Provider p = mInstalledProviders.get(i);
1084 if (p.instances.size() > 0) {
1085 out.startTag(null, "p");
1086 out.attribute(null, "pkg", p.info.provider.getPackageName());
1087 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001088 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 p.tag = providerIndex;
1090 providerIndex++;
1091 }
1092 }
1093
1094 N = mHosts.size();
1095 for (int i=0; i<N; i++) {
1096 Host host = mHosts.get(i);
1097 out.startTag(null, "h");
1098 out.attribute(null, "pkg", host.packageName);
1099 out.attribute(null, "id", Integer.toHexString(host.hostId));
1100 out.endTag(null, "h");
1101 host.tag = i;
1102 }
1103
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001104 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001106 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001108 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1110 if (id.provider != null) {
1111 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1112 }
1113 out.endTag(null, "g");
1114 }
1115
1116 out.endTag(null, "gs");
1117
1118 out.endDocument();
1119 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001120 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 } catch (IOException e) {
1122 try {
1123 if (stream != null) {
1124 stream.close();
1125 }
1126 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001127 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 }
1129 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001130 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 file.delete();
1132 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001133 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 }
1135 }
1136
1137 void readStateFromFileLocked(File file) {
1138 FileInputStream stream = null;
1139
1140 boolean success = false;
1141
1142 try {
1143 stream = new FileInputStream(file);
1144 XmlPullParser parser = Xml.newPullParser();
1145 parser.setInput(stream, null);
1146
1147 int type;
1148 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001149 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 do {
1151 type = parser.next();
1152 if (type == XmlPullParser.START_TAG) {
1153 String tag = parser.getName();
1154 if ("p".equals(tag)) {
1155 // TODO: do we need to check that this package has the same signature
1156 // as before?
1157 String pkg = parser.getAttributeValue(null, "pkg");
1158 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001159
1160 final PackageManager packageManager = mContext.getPackageManager();
1161 try {
1162 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1163 } catch (PackageManager.NameNotFoundException e) {
1164 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1165 new String[] { pkg });
1166 pkg = pkgs[0];
1167 }
1168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1170 if (p == null && mSafeMode) {
1171 // if we're in safe mode, make a temporary one
1172 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001173 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 p.info.provider = new ComponentName(pkg, cl);
1175 p.zombie = true;
1176 mInstalledProviders.add(p);
1177 }
1178 if (p != null) {
1179 // if it wasn't uninstalled or something
1180 loadedProviders.put(providerIndex, p);
1181 }
1182 providerIndex++;
1183 }
1184 else if ("h".equals(tag)) {
1185 Host host = new Host();
1186
1187 // TODO: do we need to check that this package has the same signature
1188 // as before?
1189 host.packageName = parser.getAttributeValue(null, "pkg");
1190 try {
1191 host.uid = getUidForPackage(host.packageName);
1192 } catch (PackageManager.NameNotFoundException ex) {
1193 host.zombie = true;
1194 }
1195 if (!host.zombie || mSafeMode) {
1196 // In safe mode, we don't discard the hosts we don't recognize
1197 // so that they're not pruned from our list. Otherwise, we do.
1198 host.hostId = Integer.parseInt(
1199 parser.getAttributeValue(null, "id"), 16);
1200 mHosts.add(host);
1201 }
1202 }
1203 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001204 AppWidgetId id = new AppWidgetId();
1205 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1206 if (id.appWidgetId >= mNextAppWidgetId) {
1207 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 }
1209
1210 String providerString = parser.getAttributeValue(null, "p");
1211 if (providerString != null) {
1212 // there's no provider if it hasn't been bound yet.
1213 // maybe we don't have to save this, but it brings the system
1214 // to the state it was in.
1215 int pIndex = Integer.parseInt(providerString, 16);
1216 id.provider = loadedProviders.get(pIndex);
1217 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001218 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 + pIndex + " which is " + id.provider);
1220 }
1221 if (id.provider == null) {
1222 // This provider is gone. We just let the host figure out
1223 // that this happened when it fails to load it.
1224 continue;
1225 }
1226 }
1227
1228 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1229 id.host = mHosts.get(hIndex);
1230 if (id.host == null) {
1231 // This host is gone.
1232 continue;
1233 }
1234
1235 if (id.provider != null) {
1236 id.provider.instances.add(id);
1237 }
1238 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001239 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001240 }
1241 }
1242 } while (type != XmlPullParser.END_DOCUMENT);
1243 success = true;
1244 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001245 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001246 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001247 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001249 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001251 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001253 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001254 }
1255 try {
1256 if (stream != null) {
1257 stream.close();
1258 }
1259 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001260 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 }
1262
1263 if (success) {
1264 // delete any hosts that didn't manage to get connected (should happen)
1265 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001266 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 pruneHostLocked(mHosts.get(i));
1268 }
1269 } else {
1270 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001271 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001272 mHosts.clear();
1273 final int N = mInstalledProviders.size();
1274 for (int i=0; i<N; i++) {
1275 mInstalledProviders.get(i).instances.clear();
1276 }
1277 }
1278 }
1279
1280 File savedStateTempFile() {
1281 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1282 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1283 }
1284
1285 File savedStateRealFile() {
1286 return new File("/data/system/" + SETTINGS_FILENAME);
1287 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1288 }
1289
1290 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1291 public void onReceive(Context context, Intent intent) {
1292 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001293 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1295 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001296 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1297 Locale revised = Locale.getDefault();
1298 if (revised == null || mLocale == null ||
1299 !(revised.equals(mLocale))) {
1300 mLocale = revised;
1301
1302 synchronized (mAppWidgetIds) {
1303 int N = mInstalledProviders.size();
1304 for (int i=N-1; i>=0; i--) {
1305 Provider p = mInstalledProviders.get(i);
1306 String pkgName = p.info.provider.getPackageName();
1307 updateProvidersForPackageLocked(pkgName);
1308 }
1309 saveStateLocked();
1310 }
1311 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001313 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001314 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001315 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001316 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001317 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1318 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001319 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001320 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1321 added = false;
1322 } else {
1323 Uri uri = intent.getData();
1324 if (uri == null) {
1325 return;
1326 }
1327 String pkgName = uri.getSchemeSpecificPart();
1328 if (pkgName == null) {
1329 return;
1330 }
1331 pkgList = new String[] { pkgName };
1332 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001333 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001334 }
1335 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 return;
1337 }
Joe Onoratod070e892011-01-07 20:50:37 -08001338 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001339 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001341 if (changed || (extras != null &&
1342 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001343 for (String pkgName : pkgList) {
1344 // The package was just upgraded
1345 updateProvidersForPackageLocked(pkgName);
1346 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 } else {
1348 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001349 for (String pkgName : pkgList) {
1350 addProvidersForPackageLocked(pkgName);
1351 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352 }
1353 saveStateLocked();
1354 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001355 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 Bundle extras = intent.getExtras();
1357 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1358 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1359 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001360 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001361 for (String pkgName : pkgList) {
1362 removeProvidersForPackageLocked(pkgName);
1363 saveStateLocked();
1364 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 }
1366 }
1367 }
1368 }
1369 }
1370 };
1371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001373 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001374 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1376 PackageManager.GET_META_DATA);
1377
Bjorn Bringert5f857802010-02-10 23:09:48 +00001378 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379 for (int i=0; i<N; i++) {
1380 ResolveInfo ri = broadcastReceivers.get(i);
1381 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001382 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1383 continue;
1384 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 if (pkgName.equals(ai.packageName)) {
1386 addProviderLocked(ri);
1387 }
1388 }
1389 }
1390
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001392 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001393 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001394 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1396 PackageManager.GET_META_DATA);
1397
1398 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001399 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 for (int i=0; i<N; i++) {
1401 ResolveInfo ri = broadcastReceivers.get(i);
1402 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001403 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1404 continue;
1405 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 if (pkgName.equals(ai.packageName)) {
1407 ComponentName component = new ComponentName(ai.packageName, ai.name);
1408 Provider p = lookupProviderLocked(component);
1409 if (p == null) {
1410 if (addProviderLocked(ri)) {
1411 keep.add(ai.name);
1412 }
1413 } else {
1414 Provider parsed = parseProviderInfoXml(component, ri);
1415 if (parsed != null) {
1416 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001417 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001418 p.info = parsed.info;
1419 // If it's enabled
1420 final int M = p.instances.size();
1421 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001422 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 // Reschedule for the new updatePeriodMillis (don't worry about handling
1424 // it specially if updatePeriodMillis didn't change because we just sent
1425 // an update, and the next one will be updatePeriodMillis from now).
1426 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001427 registerForBroadcastsLocked(p, appWidgetIds);
1428 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001430 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001431 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 if (id.host != null && id.host.callbacks != null) {
1433 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001434 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435 } catch (RemoteException ex) {
1436 // It failed; remove the callback. No need to prune because
1437 // we know that this host is still referenced by this
1438 // instance.
1439 id.host.callbacks = null;
1440 }
1441 }
1442 }
1443 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001444 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001445 }
1446 }
1447 }
1448 }
1449 }
1450
1451 // prune the ones we don't want to keep
1452 N = mInstalledProviders.size();
1453 for (int i=N-1; i>=0; i--) {
1454 Provider p = mInstalledProviders.get(i);
1455 if (pkgName.equals(p.info.provider.getPackageName())
1456 && !keep.contains(p.info.provider.getClassName())) {
1457 removeProviderLocked(i, p);
1458 }
1459 }
1460 }
1461
1462 void removeProvidersForPackageLocked(String pkgName) {
1463 int N = mInstalledProviders.size();
1464 for (int i=N-1; i>=0; i--) {
1465 Provider p = mInstalledProviders.get(i);
1466 if (pkgName.equals(p.info.provider.getPackageName())) {
1467 removeProviderLocked(i, p);
1468 }
1469 }
1470
1471 // Delete the hosts for this package too
1472 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001473 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001474 // so we don't need to worry about sending DISABLE broadcasts to them.
1475 N = mHosts.size();
1476 for (int i=N-1; i>=0; i--) {
1477 Host host = mHosts.get(i);
1478 if (pkgName.equals(host.packageName)) {
1479 deleteHostLocked(host);
1480 }
1481 }
1482 }
1483}
1484