blob: 59a540b8f1751cc6365dd93fe5126e169421a2d7 [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;
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.HashSet;
28import java.util.Iterator;
29import java.util.List;
30import java.util.Locale;
31
32import org.xmlpull.v1.XmlPullParser;
33import org.xmlpull.v1.XmlPullParserException;
34import org.xmlpull.v1.XmlSerializer;
35
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.app.AlarmManager;
37import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070038import android.appwidget.AppWidgetManager;
39import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.content.BroadcastReceiver;
41import android.content.ComponentName;
42import android.content.Context;
43import android.content.Intent;
Winson Chung81f39eb2011-01-11 18:05:01 -080044import android.content.Intent.FilterComparison;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.content.IntentFilter;
Winson Chung81f39eb2011-01-11 18:05:01 -080046import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.content.pm.ActivityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -040048import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.content.pm.PackageInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080050import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.content.pm.ResolveInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080052import android.content.pm.ServiceInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080053import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.content.res.TypedArray;
55import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.net.Uri;
57import android.os.Binder;
58import android.os.Bundle;
Winson Chung81f39eb2011-01-11 18:05:01 -080059import android.os.Handler;
60import android.os.IBinder;
Marco Nelissen54796e72009-04-30 15:16:30 -070061import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.os.RemoteException;
63import android.os.SystemClock;
64import android.util.AttributeSet;
Winson Chung81f39eb2011-01-11 18:05:01 -080065import android.util.Pair;
Joe Onorato8a9b2202010-02-26 18:56:32 -080066import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070067import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.util.Xml;
69import android.widget.RemoteViews;
70
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070071import com.android.internal.appwidget.IAppWidgetHost;
Winson Chung81f39eb2011-01-11 18:05:01 -080072import com.android.internal.appwidget.IAppWidgetService;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080073import com.android.internal.util.FastXmlSerializer;
Winson Chung81f39eb2011-01-11 18:05:01 -080074import com.android.internal.widget.IRemoteViewsAdapterConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070076class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070078 private static final String TAG = "AppWidgetService";
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 SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070082 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083
84 /*
85 * When identifying a Host or Provider based on the calling process, use the uid field.
86 * When identifying a Host or Provider based on a package manager broadcast, use the
87 * package given.
88 */
89
90 static class Provider {
91 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070092 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070093 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 PendingIntent broadcast;
95 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
96
97 int tag; // for use while saving state (the index)
98 }
99
100 static class Host {
101 int uid;
102 int hostId;
103 String packageName;
Romain Guya5475592009-07-01 17:20:08 -0700104 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700105 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
107
108 int tag; // for use while saving state (the index)
109 }
110
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700111 static class AppWidgetId {
112 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 Provider provider;
114 RemoteViews views;
115 Host host;
116 }
117
Winson Chung81f39eb2011-01-11 18:05:01 -0800118 /**
119 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
120 * This needs to be a static inner class since a reference to the ServiceConnection is held
121 * globally and may lead us to leak AppWidgetService instances (if there were more than one).
122 */
123 static class ServiceConnectionProxy implements ServiceConnection {
124 private final AppWidgetService mAppWidgetService;
125 private final Pair<Integer, Intent.FilterComparison> mKey;
126 private final IBinder mConnectionCb;
127
128 ServiceConnectionProxy(AppWidgetService appWidgetService,
129 Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
130 mAppWidgetService = appWidgetService;
131 mKey = key;
132 mConnectionCb = connectionCb;
133 }
134 public void onServiceConnected(ComponentName name, IBinder service) {
135 IRemoteViewsAdapterConnection cb =
136 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
137 try {
138 cb.onServiceConnected(service);
139 } catch (RemoteException e) {
140 e.printStackTrace();
141 }
142 }
143 public void onServiceDisconnected(ComponentName name) {
144 IRemoteViewsAdapterConnection cb =
145 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
146 try {
147 cb.onServiceDisconnected();
148 mAppWidgetService.mServiceConnectionUpdateHandler.post(new Runnable() {
149 public void run() {
150 // We don't want to touch mBoundRemoteViewsServices from any other thread
151 // so queue this to run on the main thread.
152 if (mAppWidgetService.mBoundRemoteViewsServices.containsKey(mKey)) {
153 mAppWidgetService.mBoundRemoteViewsServices.remove(mKey);
154 }
155 }
156 });
157 } catch (RemoteException e) {
158 e.printStackTrace();
159 }
160 }
161 }
162
163 // Manages connections to RemoteViewsServices
164 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
165 mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
166 private final Handler mServiceConnectionUpdateHandler = new Handler();
167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700169 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 PackageManager mPackageManager;
171 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700172 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700173 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700174 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
175 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 boolean mSafeMode;
177
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700178 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 mContext = context;
180 mPackageManager = context.getPackageManager();
181 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
182 }
183
184 public void systemReady(boolean safeMode) {
185 mSafeMode = safeMode;
186
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700187 loadAppWidgetList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 loadStateLocked();
189
190 // Register for the boot completed broadcast, so we can send the
191 // ENABLE broacasts. If we try to send them now, they time out,
192 // because the system isn't ready to handle them yet.
193 mContext.registerReceiver(mBroadcastReceiver,
194 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
195
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700196 // Register for configuration changes so we can update the names
197 // of the widgets when the locale changes.
198 mContext.registerReceiver(mBroadcastReceiver,
199 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 // Register for broadcasts about package install, etc., so we can
202 // update the provider list.
203 IntentFilter filter = new IntentFilter();
204 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
Joe Onoratod070e892011-01-07 20:50:37 -0800205 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
207 filter.addDataScheme("package");
208 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800209 // Register for events related to sdcard installation.
210 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800211 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
212 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800213 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 }
215
216 @Override
217 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
218 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
219 != PackageManager.PERMISSION_GRANTED) {
220 pw.println("Permission Denial: can't dump from from pid="
221 + Binder.getCallingPid()
222 + ", uid=" + Binder.getCallingUid());
223 return;
224 }
225
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700226 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700228 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 for (int i=0; i<N; i++) {
230 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700231 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700232 pw.print(" ["); pw.print(i); pw.print("] provider ");
233 pw.print(info.provider.flattenToShortString());
234 pw.println(':');
235 pw.print(" min=("); pw.print(info.minWidth);
236 pw.print("x"); pw.print(info.minHeight);
237 pw.print(") updatePeriodMillis=");
238 pw.print(info.updatePeriodMillis);
239 pw.print(" initialLayout=#");
240 pw.print(Integer.toHexString(info.initialLayout));
241 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 }
243
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700244 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700245 pw.println(" ");
246 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700248 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700249 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700250 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700251 pw.print(" hostId=");
252 pw.print(id.host.hostId); pw.print(' ');
253 pw.print(id.host.packageName); pw.print('/');
254 pw.println(id.host.uid);
255 if (id.provider != null) {
256 pw.print(" provider=");
257 pw.println(id.provider.info.provider.flattenToShortString());
258 }
259 if (id.host != null) {
260 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
261 }
262 if (id.views != null) {
263 pw.print(" views="); pw.println(id.views);
264 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 }
266
267 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700268 pw.println(" ");
269 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 for (int i=0; i<N; i++) {
271 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700272 pw.print(" ["); pw.print(i); pw.print("] hostId=");
273 pw.print(host.hostId); pw.print(' ');
274 pw.print(host.packageName); pw.print('/');
275 pw.print(host.uid); pw.println(':');
276 pw.print(" callbacks="); pw.println(host.callbacks);
277 pw.print(" instances.size="); pw.print(host.instances.size());
278 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 }
280 }
281 }
282
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700283 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700285 synchronized (mAppWidgetIds) {
286 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287
288 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
289
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700290 AppWidgetId id = new AppWidgetId();
291 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 id.host = host;
293
294 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700295 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296
297 saveStateLocked();
298
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700299 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 }
301 }
302
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700303 public void deleteAppWidgetId(int appWidgetId) {
304 synchronized (mAppWidgetIds) {
305 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700307 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 saveStateLocked();
309 }
310 }
311 }
312
313 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700314 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 int callingUid = getCallingUid();
316 Host host = lookupHostLocked(callingUid, hostId);
317 if (host != null) {
318 deleteHostLocked(host);
319 saveStateLocked();
320 }
321 }
322 }
323
324 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700325 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 int callingUid = getCallingUid();
327 final int N = mHosts.size();
328 boolean changed = false;
329 for (int i=N-1; i>=0; i--) {
330 Host host = mHosts.get(i);
331 if (host.uid == callingUid) {
332 deleteHostLocked(host);
333 changed = true;
334 }
335 }
336 if (changed) {
337 saveStateLocked();
338 }
339 }
340 }
341
342 void deleteHostLocked(Host host) {
343 final int N = host.instances.size();
344 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700345 AppWidgetId id = host.instances.get(i);
346 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 }
348 host.instances.clear();
349 mHosts.remove(host);
350 // it's gone or going away, abruptly drop the callback connection
351 host.callbacks = null;
352 }
353
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700354 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800355 // We first unbind all services that are bound to this id
356 unbindAppWidgetRemoteViewsServicesLocked(id);
357
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 Host host = id.host;
359 host.instances.remove(id);
360 pruneHostLocked(host);
361
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700362 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363
364 Provider p = id.provider;
365 if (p != null) {
366 p.instances.remove(id);
367 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700368 // send the broacast saying that this appWidgetId has been deleted
369 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700371 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 mContext.sendBroadcast(intent);
373 if (p.instances.size() == 0) {
374 // cancel the future updates
375 cancelBroadcasts(p);
376
377 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700378 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 intent.setComponent(p.info.provider);
380 mContext.sendBroadcast(intent);
381 }
382 }
383 }
384 }
385
386 void cancelBroadcasts(Provider p) {
387 if (p.broadcast != null) {
388 mAlarmManager.cancel(p.broadcast);
389 long token = Binder.clearCallingIdentity();
390 try {
391 p.broadcast.cancel();
392 } finally {
393 Binder.restoreCallingIdentity(token);
394 }
395 p.broadcast = null;
396 }
397 }
398
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700399 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
400 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
401 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
402 synchronized (mAppWidgetIds) {
403 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 if (id == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700405 throw new IllegalArgumentException("bad appWidgetId");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 }
407 if (id.provider != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700408 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 + id.provider.info.provider);
410 }
411 Provider p = lookupProviderLocked(provider);
412 if (p == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700413 throw new IllegalArgumentException("not a appwidget provider: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 }
415 if (p.zombie) {
416 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
417 + " safe mode: " + provider);
418 }
419
420 id.provider = p;
421 p.instances.add(id);
422 int instancesSize = p.instances.size();
423 if (instancesSize == 1) {
424 // tell the provider that it's ready
425 sendEnableIntentLocked(p);
426 }
427
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700428 // send an update now -- We need this update now, and just for this appWidgetId.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 // It's less critical when the next one happens, so when we schdule the next one,
430 // we add updatePeriodMillis to its start time. That time will have some slop,
431 // but that's okay.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700432 sendUpdateIntentLocked(p, new int[] { appWidgetId });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433
434 // schedule the future updates
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700435 registerForBroadcastsLocked(p, getAppWidgetIds(p));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 saveStateLocked();
437 }
438 }
439
Winson Chung81f39eb2011-01-11 18:05:01 -0800440 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
441 synchronized (mAppWidgetIds) {
442 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
443 if (id == null) {
444 throw new IllegalArgumentException("bad appWidgetId");
445 }
446 final ComponentName componentName = intent.getComponent();
447 try {
448 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
449 PackageManager.GET_PERMISSIONS);
450 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
451 throw new SecurityException("Selected service does not require "
452 + android.Manifest.permission.BIND_REMOTEVIEWS
453 + ": " + componentName);
454 }
455 } catch (PackageManager.NameNotFoundException e) {
456 throw new IllegalArgumentException("Unknown component " + componentName);
457 }
458
459 // Bind to the RemoteViewsService (which will trigger a callback to the
460 // RemoteViewsAdapter)
461 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
462 new FilterComparison(intent));
463 final ServiceConnection conn = new ServiceConnectionProxy(this, key, connection);
464 final long token = Binder.clearCallingIdentity();
465 try {
466 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
467 mBoundRemoteViewsServices.put(key, conn);
468 } finally {
469 Binder.restoreCallingIdentity(token);
470 }
471 }
472 }
473
474 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
475 synchronized (mAppWidgetIds) {
476 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
477 if (id == null) {
478 throw new IllegalArgumentException("bad appWidgetId");
479 }
480
481 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
482 // RemoteViewsAdapter)
483 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
484 new FilterComparison(intent));
485 if (mBoundRemoteViewsServices.containsKey(key)) {
486 final ServiceConnection conn = mBoundRemoteViewsServices.get(key);
Winson Chung32cf1932011-01-19 00:52:34 -0800487 mBoundRemoteViewsServices.remove(key);
Winson Chung81f39eb2011-01-11 18:05:01 -0800488 conn.onServiceDisconnected(null);
489 mContext.unbindService(conn);
Winson Chung81f39eb2011-01-11 18:05:01 -0800490 }
491 }
492 }
493
494 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
495 Iterator<Pair<Integer, Intent.FilterComparison>> it =
496 mBoundRemoteViewsServices.keySet().iterator();
497 int appWidgetId = id.appWidgetId;
498
499 // Unbind all connections to AppWidgets bound to this id
500 while (it.hasNext()) {
501 final Pair<Integer, Intent.FilterComparison> key = it.next();
502 if (key.first.intValue() == appWidgetId) {
503 final ServiceConnection conn = mBoundRemoteViewsServices.get(key);
504 it.remove();
505 conn.onServiceDisconnected(null);
506 mContext.unbindService(conn);
507 }
508 }
509 }
510
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700511 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
512 synchronized (mAppWidgetIds) {
513 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 if (id != null && id.provider != null && !id.provider.zombie) {
515 return id.provider.info;
516 }
517 return null;
518 }
519 }
520
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700521 public RemoteViews getAppWidgetViews(int appWidgetId) {
522 synchronized (mAppWidgetIds) {
523 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 if (id != null) {
525 return id.views;
526 }
527 return null;
528 }
529 }
530
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700531 public List<AppWidgetProviderInfo> getInstalledProviders() {
532 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700534 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 for (int i=0; i<N; i++) {
536 Provider p = mInstalledProviders.get(i);
537 if (!p.zombie) {
538 result.add(p.info);
539 }
540 }
541 return result;
542 }
543 }
544
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700545 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
546 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 return;
548 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700549 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 return;
551 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700552 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700554 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700556 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
557 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 }
559 }
560 }
561
Adam Cohen2dd21972010-08-15 18:20:04 -0700562 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
563 if (appWidgetIds == null) {
564 return;
565 }
566 if (appWidgetIds.length == 0) {
567 return;
568 }
569 final int N = appWidgetIds.length;
570
571 synchronized (mAppWidgetIds) {
572 for (int i=0; i<N; i++) {
573 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
574 updateAppWidgetInstanceLocked(id, views, true);
575 }
576 }
577 }
578
Winson Chung6394c0e2010-08-16 10:14:56 -0700579 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700580 if (appWidgetIds == null) {
581 return;
582 }
583 if (appWidgetIds.length == 0) {
584 return;
585 }
586 final int N = appWidgetIds.length;
587
588 synchronized (mAppWidgetIds) {
589 for (int i=0; i<N; i++) {
590 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700591 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700592 }
593 }
594 }
595
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700596 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
597 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 Provider p = lookupProviderLocked(provider);
599 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800600 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 return;
602 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700603 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 final int N = instances.size();
605 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700606 AppWidgetId id = instances.get(i);
607 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 }
609 }
610 }
611
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700612 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700613 updateAppWidgetInstanceLocked(id, views, false);
614 }
615
616 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700617 // allow for stale appWidgetIds and other badness
618 // lookup also checks that the calling process can access the appWidgetId
619 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700621
622 // We do not want to save this RemoteViews
623 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624
625 // is anyone listening?
626 if (id.host.callbacks != null) {
627 try {
628 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700629 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 } catch (RemoteException e) {
631 // It failed; remove the callback. No need to prune because
632 // we know that this host is still referenced by this instance.
633 id.host.callbacks = null;
634 }
635 }
636 }
637 }
638
Winson Chung6394c0e2010-08-16 10:14:56 -0700639 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700640 // allow for stale appWidgetIds and other badness
641 // lookup also checks that the calling process can access the appWidgetId
642 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
643 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700644 // is anyone listening?
645 if (id.host.callbacks != null) {
646 try {
647 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700648 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700649 } catch (RemoteException e) {
650 // It failed; remove the callback. No need to prune because
651 // we know that this host is still referenced by this instance.
652 id.host.callbacks = null;
653 }
654 }
655 }
656 }
657
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700658 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 List<RemoteViews> updatedViews) {
660 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700661 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
663 host.callbacks = callbacks;
664
665 updatedViews.clear();
666
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700667 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 int N = instances.size();
669 int[] updatedIds = new int[N];
670 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700671 AppWidgetId id = instances.get(i);
672 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 updatedViews.add(id.views);
674 }
675 return updatedIds;
676 }
677 }
678
679 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700680 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700682 if (host != null) {
683 host.callbacks = null;
684 pruneHostLocked(host);
685 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 }
687 }
688
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700689 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700691 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 return true;
693 }
694 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700695 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 return true;
697 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700698 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700700 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 return true;
702 }
703 // Nobody else can access it.
704 return false;
705 }
706
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700707 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700709 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700711 AppWidgetId id = mAppWidgetIds.get(i);
712 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800713 return id;
714 }
715 }
716 return null;
717 }
718
719 Provider lookupProviderLocked(ComponentName provider) {
Romain Guyd2671e12010-03-11 18:06:42 -0800720 final String className = provider.getClassName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 final int N = mInstalledProviders.size();
722 for (int i=0; i<N; i++) {
723 Provider p = mInstalledProviders.get(i);
Romain Guyd2671e12010-03-11 18:06:42 -0800724 if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 return p;
726 }
727 }
728 return null;
729 }
730
731 Host lookupHostLocked(int uid, int hostId) {
732 final int N = mHosts.size();
733 for (int i=0; i<N; i++) {
734 Host h = mHosts.get(i);
735 if (h.uid == uid && h.hostId == hostId) {
736 return h;
737 }
738 }
739 return null;
740 }
741
742 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
743 final int N = mHosts.size();
744 for (int i=0; i<N; i++) {
745 Host h = mHosts.get(i);
746 if (h.hostId == hostId && h.packageName.equals(packageName)) {
747 return h;
748 }
749 }
750 Host host = new Host();
751 host.packageName = packageName;
752 host.uid = uid;
753 host.hostId = hostId;
754 mHosts.add(host);
755 return host;
756 }
757
758 void pruneHostLocked(Host host) {
759 if (host.instances.size() == 0 && host.callbacks == null) {
760 mHosts.remove(host);
761 }
762 }
763
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700764 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 PackageManager pm = mPackageManager;
766
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700767 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
769 PackageManager.GET_META_DATA);
770
Bjorn Bringert5f857802010-02-10 23:09:48 +0000771 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 for (int i=0; i<N; i++) {
773 ResolveInfo ri = broadcastReceivers.get(i);
774 addProviderLocked(ri);
775 }
776 }
777
778 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800779 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
780 return false;
781 }
782 if (!ri.activityInfo.isEnabled()) {
783 return false;
784 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
786 ri.activityInfo.name), ri);
787 if (p != null) {
788 mInstalledProviders.add(p);
789 return true;
790 } else {
791 return false;
792 }
793 }
794
795 void removeProviderLocked(int index, Provider p) {
796 int N = p.instances.size();
797 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700798 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700800 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 // Stop telling the host about updates for this from now on
802 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700803 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700805 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 id.provider = null;
807 pruneHostLocked(id.host);
808 id.host = null;
809 }
810 p.instances.clear();
811 mInstalledProviders.remove(index);
812 // no need to send the DISABLE broadcast, since the receiver is gone anyway
813 cancelBroadcasts(p);
814 }
815
816 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700817 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 intent.setComponent(p.info.provider);
819 mContext.sendBroadcast(intent);
820 }
821
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700822 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
823 if (appWidgetIds != null && appWidgetIds.length > 0) {
824 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
825 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 intent.setComponent(p.info.provider);
827 mContext.sendBroadcast(intent);
828 }
829 }
830
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700831 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 if (p.info.updatePeriodMillis > 0) {
833 // if this is the first instance, set the alarm. otherwise,
834 // rely on the fact that we've already set it and that
835 // PendingIntent.getBroadcast will update the extras.
836 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700837 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
838 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 intent.setComponent(p.info.provider);
840 long token = Binder.clearCallingIdentity();
841 try {
842 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
843 PendingIntent.FLAG_UPDATE_CURRENT);
844 } finally {
845 Binder.restoreCallingIdentity(token);
846 }
847 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700848 long period = p.info.updatePeriodMillis;
849 if (period < MIN_UPDATE_PERIOD) {
850 period = MIN_UPDATE_PERIOD;
851 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700853 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 }
855 }
856 }
857
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700858 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700860 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700862 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700864 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 }
866
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700867 public int[] getAppWidgetIds(ComponentName provider) {
868 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 Provider p = lookupProviderLocked(provider);
870 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700871 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 } else {
873 return new int[0];
874 }
875 }
876 }
877
878 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
879 Provider p = null;
880
881 ActivityInfo activityInfo = ri.activityInfo;
882 XmlResourceParser parser = null;
883 try {
884 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700885 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800887 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700888 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 return null;
890 }
891
892 AttributeSet attrs = Xml.asAttributeSet(parser);
893
894 int type;
895 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
896 && type != XmlPullParser.START_TAG) {
897 // drain whitespace, comments, etc.
898 }
899
900 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700901 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800902 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700903 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 return null;
905 }
906
907 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700908 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
Romain Guyd2671e12010-03-11 18:06:42 -0800909 // If metaData was null, we would have returned earlier when getting
910 // the parser No need to do the check here
911 info.oldName = activityInfo.metaData.getString(
912 AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913
914 info.provider = component;
915 p.uid = activityInfo.applicationInfo.uid;
916
Dianne Hackborn20cb56e2010-03-04 00:58:29 -0800917 Resources res = mPackageManager.getResourcesForApplication(
918 activityInfo.applicationInfo);
919
920 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700921 com.android.internal.R.styleable.AppWidgetProviderInfo);
Mitsuru Oshima8f25c422009-07-01 00:10:43 -0700922
923 // These dimensions has to be resolved in the application's context.
924 // We simply send back the raw complex data, which will be
925 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
926 TypedValue value = sa.peekValue(
927 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
928 info.minWidth = value != null ? value.data : 0;
929 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
930 info.minHeight = value != null ? value.data : 0;
931
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700933 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700935 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700937 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 if (className != null) {
939 info.configure = new ComponentName(component.getPackageName(), className);
940 }
941 info.label = activityInfo.loadLabel(mPackageManager).toString();
942 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -0700943 info.previewImage = sa.getResourceId(
944 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -0700945 info.autoAdvanceViewId = sa.getResourceId(
946 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -0700947
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 sa.recycle();
949 } catch (Exception e) {
950 // Ok to catch Exception here, because anything going wrong because
951 // of what a client process passes to us should not be fatal for the
952 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800953 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 return null;
955 } finally {
956 if (parser != null) parser.close();
957 }
958 return p;
959 }
960
961 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
962 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
963 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
964 throw new PackageManager.NameNotFoundException();
965 }
966 return pkgInfo.applicationInfo.uid;
967 }
968
969 int enforceCallingUid(String packageName) throws IllegalArgumentException {
970 int callingUid = getCallingUid();
971 int packageUid;
972 try {
973 packageUid = getUidForPackage(packageName);
974 } catch (PackageManager.NameNotFoundException ex) {
975 throw new IllegalArgumentException("packageName and uid don't match packageName="
976 + packageName);
977 }
Marco Nelissen54796e72009-04-30 15:16:30 -0700978 if (callingUid != packageUid && Process.supportsProcesses()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 throw new IllegalArgumentException("packageName and uid don't match packageName="
980 + packageName);
981 }
982 return callingUid;
983 }
984
985 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700986 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 final int N = mInstalledProviders.size();
988 for (int i=0; i<N; i++) {
989 Provider p = mInstalledProviders.get(i);
990 if (p.instances.size() > 0) {
991 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700992 int[] appWidgetIds = getAppWidgetIds(p);
993 sendUpdateIntentLocked(p, appWidgetIds);
994 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
996 }
997 }
998 }
999
1000 // only call from initialization -- it assumes that the data structures are all empty
1001 void loadStateLocked() {
1002 File temp = savedStateTempFile();
1003 File real = savedStateRealFile();
1004
1005 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1006 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1007 // fully written and delete it.
1008 if (real.exists()) {
1009 readStateFromFileLocked(real);
1010 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001011 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 temp.delete();
1013 }
1014 } else if (temp.exists()) {
1015 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001016 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 temp.renameTo(real);
1018 }
1019 }
1020
1021 void saveStateLocked() {
1022 File temp = savedStateTempFile();
1023 File real = savedStateRealFile();
1024
1025 if (!real.exists()) {
1026 // If the real one doesn't exist, it's either because this is the first time
1027 // or because something went wrong while copying them. In this case, we can't
1028 // trust anything that's in temp. In order to have the loadState code not
1029 // use the temporary one until it's fully written, create an empty file
1030 // for real, which will we'll shortly delete.
1031 try {
Romain Guya5475592009-07-01 17:20:08 -07001032 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 real.createNewFile();
1034 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001035 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 }
1037 }
1038
1039 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001040 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 temp.delete();
1042 }
1043
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001044 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001045 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001046 return;
1047 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048
Romain Guya5475592009-07-01 17:20:08 -07001049 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001051 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 temp.renameTo(real);
1053 }
1054
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001055 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 FileOutputStream stream = null;
1057 int N;
1058
1059 try {
1060 stream = new FileOutputStream(file, false);
1061 XmlSerializer out = new FastXmlSerializer();
1062 out.setOutput(stream, "utf-8");
1063 out.startDocument(null, true);
1064
1065
1066 out.startTag(null, "gs");
1067
1068 int providerIndex = 0;
1069 N = mInstalledProviders.size();
1070 for (int i=0; i<N; i++) {
1071 Provider p = mInstalledProviders.get(i);
1072 if (p.instances.size() > 0) {
1073 out.startTag(null, "p");
1074 out.attribute(null, "pkg", p.info.provider.getPackageName());
1075 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001076 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 p.tag = providerIndex;
1078 providerIndex++;
1079 }
1080 }
1081
1082 N = mHosts.size();
1083 for (int i=0; i<N; i++) {
1084 Host host = mHosts.get(i);
1085 out.startTag(null, "h");
1086 out.attribute(null, "pkg", host.packageName);
1087 out.attribute(null, "id", Integer.toHexString(host.hostId));
1088 out.endTag(null, "h");
1089 host.tag = i;
1090 }
1091
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001092 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001094 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001096 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1098 if (id.provider != null) {
1099 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1100 }
1101 out.endTag(null, "g");
1102 }
1103
1104 out.endTag(null, "gs");
1105
1106 out.endDocument();
1107 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001108 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 } catch (IOException e) {
1110 try {
1111 if (stream != null) {
1112 stream.close();
1113 }
1114 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001115 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 }
1117 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001118 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 file.delete();
1120 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001121 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 }
1123 }
1124
1125 void readStateFromFileLocked(File file) {
1126 FileInputStream stream = null;
1127
1128 boolean success = false;
1129
1130 try {
1131 stream = new FileInputStream(file);
1132 XmlPullParser parser = Xml.newPullParser();
1133 parser.setInput(stream, null);
1134
1135 int type;
1136 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001137 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 do {
1139 type = parser.next();
1140 if (type == XmlPullParser.START_TAG) {
1141 String tag = parser.getName();
1142 if ("p".equals(tag)) {
1143 // TODO: do we need to check that this package has the same signature
1144 // as before?
1145 String pkg = parser.getAttributeValue(null, "pkg");
1146 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001147
1148 final PackageManager packageManager = mContext.getPackageManager();
1149 try {
1150 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1151 } catch (PackageManager.NameNotFoundException e) {
1152 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1153 new String[] { pkg });
1154 pkg = pkgs[0];
1155 }
1156
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1158 if (p == null && mSafeMode) {
1159 // if we're in safe mode, make a temporary one
1160 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001161 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 p.info.provider = new ComponentName(pkg, cl);
1163 p.zombie = true;
1164 mInstalledProviders.add(p);
1165 }
1166 if (p != null) {
1167 // if it wasn't uninstalled or something
1168 loadedProviders.put(providerIndex, p);
1169 }
1170 providerIndex++;
1171 }
1172 else if ("h".equals(tag)) {
1173 Host host = new Host();
1174
1175 // TODO: do we need to check that this package has the same signature
1176 // as before?
1177 host.packageName = parser.getAttributeValue(null, "pkg");
1178 try {
1179 host.uid = getUidForPackage(host.packageName);
1180 } catch (PackageManager.NameNotFoundException ex) {
1181 host.zombie = true;
1182 }
1183 if (!host.zombie || mSafeMode) {
1184 // In safe mode, we don't discard the hosts we don't recognize
1185 // so that they're not pruned from our list. Otherwise, we do.
1186 host.hostId = Integer.parseInt(
1187 parser.getAttributeValue(null, "id"), 16);
1188 mHosts.add(host);
1189 }
1190 }
1191 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001192 AppWidgetId id = new AppWidgetId();
1193 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1194 if (id.appWidgetId >= mNextAppWidgetId) {
1195 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 }
1197
1198 String providerString = parser.getAttributeValue(null, "p");
1199 if (providerString != null) {
1200 // there's no provider if it hasn't been bound yet.
1201 // maybe we don't have to save this, but it brings the system
1202 // to the state it was in.
1203 int pIndex = Integer.parseInt(providerString, 16);
1204 id.provider = loadedProviders.get(pIndex);
1205 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001206 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 + pIndex + " which is " + id.provider);
1208 }
1209 if (id.provider == null) {
1210 // This provider is gone. We just let the host figure out
1211 // that this happened when it fails to load it.
1212 continue;
1213 }
1214 }
1215
1216 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1217 id.host = mHosts.get(hIndex);
1218 if (id.host == null) {
1219 // This host is gone.
1220 continue;
1221 }
1222
1223 if (id.provider != null) {
1224 id.provider.instances.add(id);
1225 }
1226 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001227 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 }
1229 }
1230 } while (type != XmlPullParser.END_DOCUMENT);
1231 success = true;
1232 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001233 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001234 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001235 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001237 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001239 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001240 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001241 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 }
1243 try {
1244 if (stream != null) {
1245 stream.close();
1246 }
1247 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001248 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249 }
1250
1251 if (success) {
1252 // delete any hosts that didn't manage to get connected (should happen)
1253 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001254 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 pruneHostLocked(mHosts.get(i));
1256 }
1257 } else {
1258 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001259 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260 mHosts.clear();
1261 final int N = mInstalledProviders.size();
1262 for (int i=0; i<N; i++) {
1263 mInstalledProviders.get(i).instances.clear();
1264 }
1265 }
1266 }
1267
1268 File savedStateTempFile() {
1269 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1270 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1271 }
1272
1273 File savedStateRealFile() {
1274 return new File("/data/system/" + SETTINGS_FILENAME);
1275 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1276 }
1277
1278 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1279 public void onReceive(Context context, Intent intent) {
1280 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001281 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1283 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001284 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1285 Locale revised = Locale.getDefault();
1286 if (revised == null || mLocale == null ||
1287 !(revised.equals(mLocale))) {
1288 mLocale = revised;
1289
1290 synchronized (mAppWidgetIds) {
1291 int N = mInstalledProviders.size();
1292 for (int i=N-1; i>=0; i--) {
1293 Provider p = mInstalledProviders.get(i);
1294 String pkgName = p.info.provider.getPackageName();
1295 updateProvidersForPackageLocked(pkgName);
1296 }
1297 saveStateLocked();
1298 }
1299 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001301 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001302 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001303 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001304 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001305 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1306 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001307 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001308 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1309 added = false;
1310 } else {
1311 Uri uri = intent.getData();
1312 if (uri == null) {
1313 return;
1314 }
1315 String pkgName = uri.getSchemeSpecificPart();
1316 if (pkgName == null) {
1317 return;
1318 }
1319 pkgList = new String[] { pkgName };
1320 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001321 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001322 }
1323 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 return;
1325 }
Joe Onoratod070e892011-01-07 20:50:37 -08001326 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001327 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001328 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001329 if (changed || (extras != null &&
1330 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001331 for (String pkgName : pkgList) {
1332 // The package was just upgraded
1333 updateProvidersForPackageLocked(pkgName);
1334 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335 } else {
1336 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001337 for (String pkgName : pkgList) {
1338 addProvidersForPackageLocked(pkgName);
1339 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001340 }
1341 saveStateLocked();
1342 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001343 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 Bundle extras = intent.getExtras();
1345 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1346 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1347 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001348 synchronized (mAppWidgetIds) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001349 for (String pkgName : pkgList) {
1350 removeProvidersForPackageLocked(pkgName);
1351 saveStateLocked();
1352 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001353 }
1354 }
1355 }
1356 }
1357 }
1358 };
1359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001360 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001361 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001362 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1364 PackageManager.GET_META_DATA);
1365
Bjorn Bringert5f857802010-02-10 23:09:48 +00001366 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 for (int i=0; i<N; i++) {
1368 ResolveInfo ri = broadcastReceivers.get(i);
1369 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001370 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1371 continue;
1372 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 if (pkgName.equals(ai.packageName)) {
1374 addProviderLocked(ri);
1375 }
1376 }
1377 }
1378
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001380 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001381 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001382 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1384 PackageManager.GET_META_DATA);
1385
1386 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001387 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388 for (int i=0; i<N; i++) {
1389 ResolveInfo ri = broadcastReceivers.get(i);
1390 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001391 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1392 continue;
1393 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001394 if (pkgName.equals(ai.packageName)) {
1395 ComponentName component = new ComponentName(ai.packageName, ai.name);
1396 Provider p = lookupProviderLocked(component);
1397 if (p == null) {
1398 if (addProviderLocked(ri)) {
1399 keep.add(ai.name);
1400 }
1401 } else {
1402 Provider parsed = parseProviderInfoXml(component, ri);
1403 if (parsed != null) {
1404 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001405 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 p.info = parsed.info;
1407 // If it's enabled
1408 final int M = p.instances.size();
1409 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001410 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001411 // Reschedule for the new updatePeriodMillis (don't worry about handling
1412 // it specially if updatePeriodMillis didn't change because we just sent
1413 // an update, and the next one will be updatePeriodMillis from now).
1414 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001415 registerForBroadcastsLocked(p, appWidgetIds);
1416 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001418 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001419 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001420 if (id.host != null && id.host.callbacks != null) {
1421 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001422 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 } catch (RemoteException ex) {
1424 // It failed; remove the callback. No need to prune because
1425 // we know that this host is still referenced by this
1426 // instance.
1427 id.host.callbacks = null;
1428 }
1429 }
1430 }
1431 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001432 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001433 }
1434 }
1435 }
1436 }
1437 }
1438
1439 // prune the ones we don't want to keep
1440 N = mInstalledProviders.size();
1441 for (int i=N-1; i>=0; i--) {
1442 Provider p = mInstalledProviders.get(i);
1443 if (pkgName.equals(p.info.provider.getPackageName())
1444 && !keep.contains(p.info.provider.getClassName())) {
1445 removeProviderLocked(i, p);
1446 }
1447 }
1448 }
1449
1450 void removeProvidersForPackageLocked(String pkgName) {
1451 int N = mInstalledProviders.size();
1452 for (int i=N-1; i>=0; i--) {
1453 Provider p = mInstalledProviders.get(i);
1454 if (pkgName.equals(p.info.provider.getPackageName())) {
1455 removeProviderLocked(i, p);
1456 }
1457 }
1458
1459 // Delete the hosts for this package too
1460 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001461 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 // so we don't need to worry about sending DISABLE broadcasts to them.
1463 N = mHosts.size();
1464 for (int i=N-1; i>=0; i--) {
1465 Host host = mHosts.get(i);
1466 if (pkgName.equals(host.packageName)) {
1467 deleteHostLocked(host);
1468 }
1469 }
1470 }
1471}
1472