blob: eb75ebc1d1fbaf958e35bcda6f0b3fe3de77b952 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070021import android.appwidget.AppWidgetManager;
22import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
Winson Chung81f39eb2011-01-11 18:05:01 -080028import android.content.ServiceConnection;
Adam Cohen97300312011-10-12 15:48:13 -070029import android.content.Intent.FilterComparison;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.pm.ActivityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -040031import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.pm.PackageInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080033import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.pm.ResolveInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080035import android.content.pm.ServiceInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080036import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.content.res.TypedArray;
38import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.net.Uri;
40import android.os.Binder;
41import android.os.Bundle;
Winson Chung81f39eb2011-01-11 18:05:01 -080042import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.RemoteException;
44import android.os.SystemClock;
45import android.util.AttributeSet;
Winson Chung16c8d8a2011-01-20 16:19:33 -080046import android.util.Log;
Winson Chung81f39eb2011-01-11 18:05:01 -080047import android.util.Pair;
Joe Onorato8a9b2202010-02-26 18:56:32 -080048import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070049import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.util.Xml;
51import android.widget.RemoteViews;
52
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070053import com.android.internal.appwidget.IAppWidgetHost;
Winson Chung81f39eb2011-01-11 18:05:01 -080054import com.android.internal.appwidget.IAppWidgetService;
Adam Cohen97300312011-10-12 15:48:13 -070055import com.android.internal.os.AtomicFile;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080056import com.android.internal.util.FastXmlSerializer;
Winson Chung81f39eb2011-01-11 18:05:01 -080057import com.android.internal.widget.IRemoteViewsAdapterConnection;
Winson Chung84bbb022011-02-21 13:57:45 -080058import com.android.internal.widget.IRemoteViewsFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
Adam Cohen97300312011-10-12 15:48:13 -070060import org.xmlpull.v1.XmlPullParser;
61import org.xmlpull.v1.XmlPullParserException;
62import org.xmlpull.v1.XmlSerializer;
63
64import java.io.File;
65import java.io.FileDescriptor;
66import java.io.FileInputStream;
67import java.io.FileNotFoundException;
68import java.io.FileOutputStream;
69import java.io.IOException;
70import java.io.PrintWriter;
71import java.util.ArrayList;
72import java.util.HashMap;
73import java.util.HashSet;
74import java.util.Iterator;
75import java.util.List;
76import java.util.Locale;
77
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";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070083 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
85 /*
86 * When identifying a Host or Provider based on the calling process, use the uid field.
87 * When identifying a Host or Provider based on a package manager broadcast, use the
88 * package given.
89 */
90
91 static class Provider {
92 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070093 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070094 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 PendingIntent broadcast;
96 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
97
98 int tag; // for use while saving state (the index)
99 }
100
101 static class Host {
102 int uid;
103 int hostId;
104 String packageName;
Romain Guya5475592009-07-01 17:20:08 -0700105 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700106 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
108
109 int tag; // for use while saving state (the index)
110 }
111
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700112 static class AppWidgetId {
113 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 Provider provider;
115 RemoteViews views;
116 Host host;
117 }
118
Winson Chung81f39eb2011-01-11 18:05:01 -0800119 /**
120 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
121 * This needs to be a static inner class since a reference to the ServiceConnection is held
122 * globally and may lead us to leak AppWidgetService instances (if there were more than one).
123 */
124 static class ServiceConnectionProxy implements ServiceConnection {
Winson Chung81f39eb2011-01-11 18:05:01 -0800125 private final Pair<Integer, Intent.FilterComparison> mKey;
126 private final IBinder mConnectionCb;
127
Winson Chung16c8d8a2011-01-20 16:19:33 -0800128 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800129 mKey = key;
130 mConnectionCb = connectionCb;
131 }
132 public void onServiceConnected(ComponentName name, IBinder service) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800133 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800134 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
135 try {
136 cb.onServiceConnected(service);
Adam Cohenc2be22c2011-03-16 16:33:53 -0700137 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800138 e.printStackTrace();
139 }
140 }
141 public void onServiceDisconnected(ComponentName name) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800142 disconnect();
143 }
144 public void disconnect() {
145 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800146 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
147 try {
148 cb.onServiceDisconnected();
Adam Cohenc2be22c2011-03-16 16:33:53 -0700149 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800150 e.printStackTrace();
151 }
152 }
153 }
154
Winson Chung84bbb022011-02-21 13:57:45 -0800155 // Manages active connections to RemoteViewsServices
Winson Chung81f39eb2011-01-11 18:05:01 -0800156 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
157 mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
Winson Chung84bbb022011-02-21 13:57:45 -0800158 // Manages persistent references to RemoteViewsServices from different App Widgets
159 private final HashMap<FilterComparison, HashSet<Integer>>
160 mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
Winson Chung81f39eb2011-01-11 18:05:01 -0800161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700163 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 PackageManager mPackageManager;
165 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700166 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700167 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700168 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
169 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 boolean mSafeMode;
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700171 boolean mStateLoaded;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172
Adam Cohen7bb98832011-10-05 18:10:13 -0700173 // These are for debugging only -- widgets are going missing in some rare instances
174 ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
175 ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
176
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700177 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 mContext = context;
179 mPackageManager = context.getPackageManager();
180 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
181 }
182
183 public void systemReady(boolean safeMode) {
184 mSafeMode = safeMode;
185
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700186 synchronized (mAppWidgetIds) {
187 ensureStateLoadedLocked();
188 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189
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
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700216 private void ensureStateLoadedLocked() {
217 if (!mStateLoaded) {
218 loadAppWidgetList();
219 loadStateLocked();
220 mStateLoaded = true;
221 }
222 }
223
Adam Cohen7bb98832011-10-05 18:10:13 -0700224 private void dumpProvider(Provider p, int index, PrintWriter pw) {
225 AppWidgetProviderInfo info = p.info;
226 pw.print(" ["); pw.print(index); pw.print("] provider ");
227 pw.print(info.provider.flattenToShortString());
228 pw.println(':');
229 pw.print(" min=("); pw.print(info.minWidth);
230 pw.print("x"); pw.print(info.minHeight);
231 pw.print(") minResize=("); pw.print(info.minResizeWidth);
232 pw.print("x"); pw.print(info.minResizeHeight);
233 pw.print(") updatePeriodMillis=");
234 pw.print(info.updatePeriodMillis);
235 pw.print(" resizeMode=");
236 pw.print(info.resizeMode);
237 pw.print(" autoAdvanceViewId=");
238 pw.print(info.autoAdvanceViewId);
239 pw.print(" initialLayout=#");
240 pw.print(Integer.toHexString(info.initialLayout));
241 pw.print(" zombie="); pw.println(p.zombie);
242 }
243
244 private void dumpHost(Host host, int index, PrintWriter pw) {
245 pw.print(" ["); pw.print(index); pw.print("] hostId=");
246 pw.print(host.hostId); pw.print(' ');
247 pw.print(host.packageName); pw.print('/');
248 pw.print(host.uid); pw.println(':');
249 pw.print(" callbacks="); pw.println(host.callbacks);
250 pw.print(" instances.size="); pw.print(host.instances.size());
251 pw.print(" zombie="); pw.println(host.zombie);
252 }
253
254 private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
255 pw.print(" ["); pw.print(index); pw.print("] id=");
256 pw.println(id.appWidgetId);
257 pw.print(" hostId=");
258 pw.print(id.host.hostId); pw.print(' ');
259 pw.print(id.host.packageName); pw.print('/');
260 pw.println(id.host.uid);
261 if (id.provider != null) {
262 pw.print(" provider=");
263 pw.println(id.provider.info.provider.flattenToShortString());
264 }
265 if (id.host != null) {
266 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
267 }
268 if (id.views != null) {
269 pw.print(" views="); pw.println(id.views);
270 }
271 }
272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 @Override
274 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
275 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
276 != PackageManager.PERMISSION_GRANTED) {
277 pw.println("Permission Denial: can't dump from from pid="
278 + Binder.getCallingPid()
279 + ", uid=" + Binder.getCallingUid());
280 return;
281 }
282
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700283 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700285 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700287 dumpProvider(mInstalledProviders.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 }
289
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700290 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700291 pw.println(" ");
292 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700294 dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 }
296
297 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700298 pw.println(" ");
299 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 for (int i=0; i<N; i++) {
Adam Cohen7bb98832011-10-05 18:10:13 -0700301 dumpHost(mHosts.get(i), i, pw);
302 }
303
304 N = mDeletedProviders.size();
305 pw.println(" ");
306 pw.println("Deleted Providers:");
307 for (int i=0; i<N; i++) {
308 dumpProvider(mDeletedProviders.get(i), i, pw);
309 }
310
311 N = mDeletedHosts.size();
312 pw.println(" ");
313 pw.println("Deleted Hosts:");
314 for (int i=0; i<N; i++) {
315 dumpHost(mDeletedHosts.get(i), i, pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 }
317 }
318 }
319
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700320 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700322 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700323 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700324 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325
326 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
327
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700328 AppWidgetId id = new AppWidgetId();
329 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 id.host = host;
331
332 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700333 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334
335 saveStateLocked();
336
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700337 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 }
339 }
340
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700341 public void deleteAppWidgetId(int appWidgetId) {
342 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700343 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700344 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700346 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 saveStateLocked();
348 }
349 }
350 }
351
352 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700353 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700354 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 int callingUid = getCallingUid();
356 Host host = lookupHostLocked(callingUid, hostId);
357 if (host != null) {
358 deleteHostLocked(host);
359 saveStateLocked();
360 }
361 }
362 }
363
364 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700365 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700366 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 int callingUid = getCallingUid();
368 final int N = mHosts.size();
369 boolean changed = false;
370 for (int i=N-1; i>=0; i--) {
371 Host host = mHosts.get(i);
372 if (host.uid == callingUid) {
373 deleteHostLocked(host);
374 changed = true;
375 }
376 }
377 if (changed) {
378 saveStateLocked();
379 }
380 }
381 }
382
383 void deleteHostLocked(Host host) {
384 final int N = host.instances.size();
385 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700386 AppWidgetId id = host.instances.get(i);
387 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 }
389 host.instances.clear();
390 mHosts.remove(host);
Adam Cohen7bb98832011-10-05 18:10:13 -0700391 mDeletedHosts.add(host);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 // it's gone or going away, abruptly drop the callback connection
393 host.callbacks = null;
394 }
395
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700396 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800397 // We first unbind all services that are bound to this id
398 unbindAppWidgetRemoteViewsServicesLocked(id);
399
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 Host host = id.host;
401 host.instances.remove(id);
402 pruneHostLocked(host);
403
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700404 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405
406 Provider p = id.provider;
407 if (p != null) {
408 p.instances.remove(id);
409 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700410 // send the broacast saying that this appWidgetId has been deleted
411 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700413 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 mContext.sendBroadcast(intent);
415 if (p.instances.size() == 0) {
416 // cancel the future updates
417 cancelBroadcasts(p);
418
419 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700420 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 intent.setComponent(p.info.provider);
422 mContext.sendBroadcast(intent);
423 }
424 }
425 }
426 }
427
428 void cancelBroadcasts(Provider p) {
429 if (p.broadcast != null) {
430 mAlarmManager.cancel(p.broadcast);
431 long token = Binder.clearCallingIdentity();
432 try {
433 p.broadcast.cancel();
434 } finally {
435 Binder.restoreCallingIdentity(token);
436 }
437 p.broadcast = null;
438 }
439 }
440
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700441 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
442 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
443 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
Dianne Hackborn4912f162011-06-15 17:40:44 -0700444
445 final long ident = Binder.clearCallingIdentity();
446 try {
447 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700448 ensureStateLoadedLocked();
Dianne Hackborn4912f162011-06-15 17:40:44 -0700449 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
450 if (id == null) {
451 throw new IllegalArgumentException("bad appWidgetId");
452 }
453 if (id.provider != null) {
454 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
455 + id.provider.info.provider);
456 }
457 Provider p = lookupProviderLocked(provider);
458 if (p == null) {
459 throw new IllegalArgumentException("not a appwidget provider: " + provider);
460 }
461 if (p.zombie) {
462 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
463 + " safe mode: " + provider);
464 }
465
466 id.provider = p;
467 p.instances.add(id);
468 int instancesSize = p.instances.size();
469 if (instancesSize == 1) {
470 // tell the provider that it's ready
471 sendEnableIntentLocked(p);
472 }
473
474 // send an update now -- We need this update now, and just for this appWidgetId.
475 // It's less critical when the next one happens, so when we schdule the next one,
476 // we add updatePeriodMillis to its start time. That time will have some slop,
477 // but that's okay.
478 sendUpdateIntentLocked(p, new int[] { appWidgetId });
479
480 // schedule the future updates
481 registerForBroadcastsLocked(p, getAppWidgetIds(p));
482 saveStateLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 }
Dianne Hackborn4912f162011-06-15 17:40:44 -0700484 } finally {
485 Binder.restoreCallingIdentity(ident);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
487 }
488
Winson Chung84bbb022011-02-21 13:57:45 -0800489 // Binds to a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800490 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
491 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700492 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800493 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
494 if (id == null) {
495 throw new IllegalArgumentException("bad appWidgetId");
496 }
497 final ComponentName componentName = intent.getComponent();
498 try {
499 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
500 PackageManager.GET_PERMISSIONS);
501 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
502 throw new SecurityException("Selected service does not require "
503 + android.Manifest.permission.BIND_REMOTEVIEWS
504 + ": " + componentName);
505 }
506 } catch (PackageManager.NameNotFoundException e) {
507 throw new IllegalArgumentException("Unknown component " + componentName);
508 }
509
Winson Chung16c8d8a2011-01-20 16:19:33 -0800510 // If there is already a connection made for this service intent, then disconnect from
511 // that first. (This does not allow multiple connections to the same service under
512 // the same key)
513 ServiceConnectionProxy conn = null;
Winson Chung84bbb022011-02-21 13:57:45 -0800514 FilterComparison fc = new FilterComparison(intent);
515 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800516 if (mBoundRemoteViewsServices.containsKey(key)) {
517 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
518 conn.disconnect();
519 mContext.unbindService(conn);
520 mBoundRemoteViewsServices.remove(key);
521 }
522
523 // Bind to the RemoteViewsService (which will trigger a callback to the
524 // RemoteViewsAdapter.onServiceConnected())
Winson Chung81f39eb2011-01-11 18:05:01 -0800525 final long token = Binder.clearCallingIdentity();
526 try {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800527 conn = new ServiceConnectionProxy(key, connection);
Winson Chung81f39eb2011-01-11 18:05:01 -0800528 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
529 mBoundRemoteViewsServices.put(key, conn);
530 } finally {
531 Binder.restoreCallingIdentity(token);
532 }
Winson Chung84bbb022011-02-21 13:57:45 -0800533
534 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
535 // when we can call back to the RemoteViewsService later to destroy associated
536 // factories.
Winson Chung22bc69d2011-02-25 15:13:38 -0800537 incrementAppWidgetServiceRefCount(appWidgetId, fc);
Winson Chung81f39eb2011-01-11 18:05:01 -0800538 }
539 }
540
Winson Chung84bbb022011-02-21 13:57:45 -0800541 // Unbinds from a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800542 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
543 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700544 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800545 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
546 // RemoteViewsAdapter)
547 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
548 new FilterComparison(intent));
549 if (mBoundRemoteViewsServices.containsKey(key)) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800550 // We don't need to use the appWidgetId until after we are sure there is something
551 // to unbind. Note that this may mask certain issues with apps calling unbind()
552 // more than necessary.
553 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
554 if (id == null) {
555 throw new IllegalArgumentException("bad appWidgetId");
556 }
557
558 ServiceConnectionProxy conn =
559 (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
560 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800561 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800562 mBoundRemoteViewsServices.remove(key);
563 } else {
564 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
Winson Chung81f39eb2011-01-11 18:05:01 -0800565 }
566 }
567 }
568
Winson Chung84bbb022011-02-21 13:57:45 -0800569 // Unbinds from a RemoteViewsService when we delete an app widget
Winson Chung81f39eb2011-01-11 18:05:01 -0800570 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800571 int appWidgetId = id.appWidgetId;
572 // Unbind all connections to Services bound to this AppWidgetId
Winson Chung81f39eb2011-01-11 18:05:01 -0800573 Iterator<Pair<Integer, Intent.FilterComparison>> it =
574 mBoundRemoteViewsServices.keySet().iterator();
Winson Chung81f39eb2011-01-11 18:05:01 -0800575 while (it.hasNext()) {
576 final Pair<Integer, Intent.FilterComparison> key = it.next();
577 if (key.first.intValue() == appWidgetId) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800578 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
579 mBoundRemoteViewsServices.get(key);
580 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800581 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800582 it.remove();
Winson Chung81f39eb2011-01-11 18:05:01 -0800583 }
584 }
Winson Chung84bbb022011-02-21 13:57:45 -0800585
586 // Check if we need to destroy any services (if no other app widgets are
587 // referencing the same service)
588 decrementAppWidgetServiceRefCount(appWidgetId);
589 }
590
591 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
592 private void destroyRemoteViewsService(final Intent intent) {
593 final ServiceConnection conn = new ServiceConnection() {
594 @Override
595 public void onServiceConnected(ComponentName name, IBinder service) {
596 final IRemoteViewsFactory cb =
597 IRemoteViewsFactory.Stub.asInterface(service);
598 try {
599 cb.onDestroy(intent);
Adam Cohen2625fea2011-03-23 17:24:30 -0700600 } catch (RemoteException e) {
601 e.printStackTrace();
602 } catch (RuntimeException e) {
Winson Chung84bbb022011-02-21 13:57:45 -0800603 e.printStackTrace();
604 }
605 mContext.unbindService(this);
606 }
607 @Override
608 public void onServiceDisconnected(android.content.ComponentName name) {
609 // Do nothing
610 }
611 };
612
613 // Bind to the service and remove the static intent->factory mapping in the
614 // RemoteViewsService.
615 final long token = Binder.clearCallingIdentity();
616 try {
617 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
618 } finally {
619 Binder.restoreCallingIdentity(token);
620 }
621 }
622
623 // Adds to the ref-count for a given RemoteViewsService intent
624 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
625 HashSet<Integer> appWidgetIds = null;
626 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
627 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
628 } else {
629 appWidgetIds = new HashSet<Integer>();
630 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
631 }
632 appWidgetIds.add(appWidgetId);
633 }
634
635 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
636 // the ref-count reaches zero.
637 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
638 Iterator<FilterComparison> it =
639 mRemoteViewsServicesAppWidgets.keySet().iterator();
640 while (it.hasNext()) {
641 final FilterComparison key = it.next();
642 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
643 if (ids.remove(appWidgetId)) {
644 // If we have removed the last app widget referencing this service, then we
645 // should destroy it and remove it from this set
646 if (ids.isEmpty()) {
647 destroyRemoteViewsService(key.getIntent());
648 it.remove();
649 }
650 }
651 }
Winson Chung81f39eb2011-01-11 18:05:01 -0800652 }
653
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700654 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
655 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700656 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700657 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 if (id != null && id.provider != null && !id.provider.zombie) {
659 return id.provider.info;
660 }
661 return null;
662 }
663 }
664
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700665 public RemoteViews getAppWidgetViews(int appWidgetId) {
666 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700667 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700668 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 if (id != null) {
670 return id.views;
671 }
672 return null;
673 }
674 }
675
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700676 public List<AppWidgetProviderInfo> getInstalledProviders() {
677 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700678 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700680 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 for (int i=0; i<N; i++) {
682 Provider p = mInstalledProviders.get(i);
683 if (!p.zombie) {
684 result.add(p.info);
685 }
686 }
687 return result;
688 }
689 }
690
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700691 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
692 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 return;
694 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700695 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 return;
697 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700698 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700700 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700701 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700703 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
704 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 }
706 }
707 }
708
Adam Cohen2dd21972010-08-15 18:20:04 -0700709 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
710 if (appWidgetIds == null) {
711 return;
712 }
713 if (appWidgetIds.length == 0) {
714 return;
715 }
716 final int N = appWidgetIds.length;
717
718 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700719 ensureStateLoadedLocked();
Adam Cohen2dd21972010-08-15 18:20:04 -0700720 for (int i=0; i<N; i++) {
721 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
722 updateAppWidgetInstanceLocked(id, views, true);
723 }
724 }
725 }
726
Winson Chung6394c0e2010-08-16 10:14:56 -0700727 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700728 if (appWidgetIds == null) {
729 return;
730 }
731 if (appWidgetIds.length == 0) {
732 return;
733 }
734 final int N = appWidgetIds.length;
735
736 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700737 ensureStateLoadedLocked();
Winson Chung499cb9f2010-07-16 11:18:17 -0700738 for (int i=0; i<N; i++) {
739 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700740 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700741 }
742 }
743 }
744
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700745 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
746 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700747 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800748 Provider p = lookupProviderLocked(provider);
749 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800750 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 return;
752 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700753 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 final int N = instances.size();
755 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700756 AppWidgetId id = instances.get(i);
757 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 }
759 }
760 }
761
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700762 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700763 updateAppWidgetInstanceLocked(id, views, false);
764 }
765
766 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700767 // allow for stale appWidgetIds and other badness
768 // lookup also checks that the calling process can access the appWidgetId
769 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700771
772 // We do not want to save this RemoteViews
773 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774
775 // is anyone listening?
776 if (id.host.callbacks != null) {
777 try {
778 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700779 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 } catch (RemoteException e) {
781 // It failed; remove the callback. No need to prune because
782 // we know that this host is still referenced by this instance.
783 id.host.callbacks = null;
784 }
785 }
786 }
787 }
788
Winson Chung6394c0e2010-08-16 10:14:56 -0700789 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700790 // allow for stale appWidgetIds and other badness
791 // lookup also checks that the calling process can access the appWidgetId
792 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
793 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700794 // is anyone listening?
795 if (id.host.callbacks != null) {
796 try {
797 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700798 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700799 } catch (RemoteException e) {
800 // It failed; remove the callback. No need to prune because
801 // we know that this host is still referenced by this instance.
802 id.host.callbacks = null;
803 }
804 }
805 }
806 }
807
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700808 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 List<RemoteViews> updatedViews) {
810 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700811 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700812 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
814 host.callbacks = callbacks;
815
816 updatedViews.clear();
817
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700818 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 int N = instances.size();
820 int[] updatedIds = new int[N];
821 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700822 AppWidgetId id = instances.get(i);
823 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 updatedViews.add(id.views);
825 }
826 return updatedIds;
827 }
828 }
829
830 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700831 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700832 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700834 if (host != null) {
835 host.callbacks = null;
836 pruneHostLocked(host);
837 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 }
839 }
840
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700841 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700843 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 return true;
845 }
846 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700847 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 return true;
849 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700850 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700852 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 return true;
854 }
855 // Nobody else can access it.
856 return false;
857 }
858
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700859 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700861 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700863 AppWidgetId id = mAppWidgetIds.get(i);
864 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 return id;
866 }
867 }
868 return null;
869 }
870
871 Provider lookupProviderLocked(ComponentName provider) {
872 final int N = mInstalledProviders.size();
873 for (int i=0; i<N; i++) {
874 Provider p = mInstalledProviders.get(i);
Adam Cohenbac26a122011-08-16 20:42:30 -0700875 if (p.info.provider.equals(provider)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 return p;
877 }
878 }
879 return null;
880 }
881
882 Host lookupHostLocked(int uid, int hostId) {
883 final int N = mHosts.size();
884 for (int i=0; i<N; i++) {
885 Host h = mHosts.get(i);
886 if (h.uid == uid && h.hostId == hostId) {
887 return h;
888 }
889 }
890 return null;
891 }
892
893 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
894 final int N = mHosts.size();
895 for (int i=0; i<N; i++) {
896 Host h = mHosts.get(i);
897 if (h.hostId == hostId && h.packageName.equals(packageName)) {
898 return h;
899 }
900 }
901 Host host = new Host();
902 host.packageName = packageName;
903 host.uid = uid;
904 host.hostId = hostId;
905 mHosts.add(host);
906 return host;
907 }
908
909 void pruneHostLocked(Host host) {
910 if (host.instances.size() == 0 && host.callbacks == null) {
911 mHosts.remove(host);
912 }
913 }
914
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700915 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 PackageManager pm = mPackageManager;
917
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700918 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
920 PackageManager.GET_META_DATA);
921
Bjorn Bringert5f857802010-02-10 23:09:48 +0000922 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 for (int i=0; i<N; i++) {
924 ResolveInfo ri = broadcastReceivers.get(i);
925 addProviderLocked(ri);
926 }
927 }
928
929 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800930 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
931 return false;
932 }
933 if (!ri.activityInfo.isEnabled()) {
934 return false;
935 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
937 ri.activityInfo.name), ri);
938 if (p != null) {
939 mInstalledProviders.add(p);
940 return true;
941 } else {
942 return false;
943 }
944 }
945
946 void removeProviderLocked(int index, Provider p) {
947 int N = p.instances.size();
948 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700949 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700951 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 // Stop telling the host about updates for this from now on
953 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700954 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700956 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 id.provider = null;
958 pruneHostLocked(id.host);
959 id.host = null;
960 }
961 p.instances.clear();
962 mInstalledProviders.remove(index);
Adam Cohen7bb98832011-10-05 18:10:13 -0700963 mDeletedProviders.add(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 // no need to send the DISABLE broadcast, since the receiver is gone anyway
965 cancelBroadcasts(p);
966 }
967
968 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700969 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 intent.setComponent(p.info.provider);
971 mContext.sendBroadcast(intent);
972 }
973
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700974 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
975 if (appWidgetIds != null && appWidgetIds.length > 0) {
976 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
977 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 intent.setComponent(p.info.provider);
979 mContext.sendBroadcast(intent);
980 }
981 }
982
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700983 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 if (p.info.updatePeriodMillis > 0) {
985 // if this is the first instance, set the alarm. otherwise,
986 // rely on the fact that we've already set it and that
987 // PendingIntent.getBroadcast will update the extras.
988 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700989 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
990 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800991 intent.setComponent(p.info.provider);
992 long token = Binder.clearCallingIdentity();
993 try {
994 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
995 PendingIntent.FLAG_UPDATE_CURRENT);
996 } finally {
997 Binder.restoreCallingIdentity(token);
998 }
999 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -07001000 long period = p.info.updatePeriodMillis;
1001 if (period < MIN_UPDATE_PERIOD) {
1002 period = MIN_UPDATE_PERIOD;
1003 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -07001005 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006 }
1007 }
1008 }
1009
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001010 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001012 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001014 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001016 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 }
1018
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001019 public int[] getAppWidgetIds(ComponentName provider) {
1020 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001021 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 Provider p = lookupProviderLocked(provider);
1023 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001024 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 } else {
1026 return new int[0];
1027 }
1028 }
1029 }
1030
1031 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1032 Provider p = null;
1033
1034 ActivityInfo activityInfo = ri.activityInfo;
1035 XmlResourceParser parser = null;
1036 try {
1037 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001038 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001040 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001041 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 return null;
1043 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001044
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -08001046
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 int type;
1048 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1049 && type != XmlPullParser.START_TAG) {
1050 // drain whitespace, comments, etc.
1051 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001052
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001054 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001055 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001056 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 return null;
1058 }
1059
1060 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001061 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 info.provider = component;
1063 p.uid = activityInfo.applicationInfo.uid;
1064
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001065 Resources res = mPackageManager.getResourcesForApplication(
1066 activityInfo.applicationInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001067
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001068 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001069 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001070
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001071 // These dimensions has to be resolved in the application's context.
1072 // We simply send back the raw complex data, which will be
1073 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1074 TypedValue value = sa.peekValue(
1075 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1076 info.minWidth = value != null ? value.data : 0;
1077 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1078 info.minHeight = value != null ? value.data : 0;
Adam Cohen1bfaf562011-07-19 18:05:33 -07001079 value = sa.peekValue(
1080 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1081 info.minResizeWidth = value != null ? value.data : info.minWidth;
1082 value = sa.peekValue(
1083 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1084 info.minResizeHeight = value != null ? value.data : info.minHeight;
Adam Cohend2e20de2011-02-25 12:03:37 -08001085
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001087 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001089 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001091 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 if (className != null) {
1093 info.configure = new ComponentName(component.getPackageName(), className);
1094 }
1095 info.label = activityInfo.loadLabel(mPackageManager).toString();
1096 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001097 info.previewImage = sa.getResourceId(
1098 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -07001099 info.autoAdvanceViewId = sa.getResourceId(
1100 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Adam Cohen9611f2e2011-02-28 13:39:38 -08001101 info.resizeMode = sa.getInt(
Adam Cohend2e20de2011-02-25 12:03:37 -08001102 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1103 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 sa.recycle();
1106 } catch (Exception e) {
1107 // Ok to catch Exception here, because anything going wrong because
1108 // of what a client process passes to us should not be fatal for the
1109 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001110 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 return null;
1112 } finally {
1113 if (parser != null) parser.close();
1114 }
1115 return p;
1116 }
1117
1118 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1119 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1120 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1121 throw new PackageManager.NameNotFoundException();
1122 }
1123 return pkgInfo.applicationInfo.uid;
1124 }
1125
1126 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1127 int callingUid = getCallingUid();
1128 int packageUid;
1129 try {
1130 packageUid = getUidForPackage(packageName);
1131 } catch (PackageManager.NameNotFoundException ex) {
1132 throw new IllegalArgumentException("packageName and uid don't match packageName="
1133 + packageName);
1134 }
Jeff Brown10e89712011-07-08 18:52:57 -07001135 if (callingUid != packageUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136 throw new IllegalArgumentException("packageName and uid don't match packageName="
1137 + packageName);
1138 }
1139 return callingUid;
1140 }
1141
1142 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001143 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001144 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 final int N = mInstalledProviders.size();
1146 for (int i=0; i<N; i++) {
1147 Provider p = mInstalledProviders.get(i);
1148 if (p.instances.size() > 0) {
1149 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001150 int[] appWidgetIds = getAppWidgetIds(p);
1151 sendUpdateIntentLocked(p, appWidgetIds);
1152 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 }
1154 }
1155 }
1156 }
1157
1158 // only call from initialization -- it assumes that the data structures are all empty
1159 void loadStateLocked() {
Adam Cohen97300312011-10-12 15:48:13 -07001160 AtomicFile file = savedStateFile();
1161 try {
1162 FileInputStream stream = file.openRead();
1163 readStateFromFileLocked(stream);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164
Adam Cohen97300312011-10-12 15:48:13 -07001165 if (stream != null) {
1166 try {
1167 stream.close();
1168 } catch (IOException e) {
1169 Slog.w(TAG, "Failed to close state FileInputStream " + e);
1170 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 }
Adam Cohen97300312011-10-12 15:48:13 -07001172 } catch (FileNotFoundException e) {
1173 Slog.w(TAG, "Failed to read state: " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 }
1175 }
Adam Cohen97300312011-10-12 15:48:13 -07001176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 void saveStateLocked() {
Adam Cohen97300312011-10-12 15:48:13 -07001178 AtomicFile file = savedStateFile();
1179 FileOutputStream stream;
1180 try {
1181 stream = file.startWrite();
1182 if (writeStateToFileLocked(stream)) {
1183 file.finishWrite(stream);
1184 } else {
1185 file.failWrite(stream);
1186 Slog.w(TAG, "Failed to save state, restoring backup.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 }
Adam Cohen97300312011-10-12 15:48:13 -07001188 } catch (IOException e) {
1189 Slog.w(TAG, "Failed open state file for write: " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 }
1192
Adam Cohen97300312011-10-12 15:48:13 -07001193 boolean writeStateToFileLocked(FileOutputStream stream) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 int N;
1195
1196 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 XmlSerializer out = new FastXmlSerializer();
1198 out.setOutput(stream, "utf-8");
1199 out.startDocument(null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 out.startTag(null, "gs");
1201
1202 int providerIndex = 0;
1203 N = mInstalledProviders.size();
1204 for (int i=0; i<N; i++) {
1205 Provider p = mInstalledProviders.get(i);
1206 if (p.instances.size() > 0) {
1207 out.startTag(null, "p");
1208 out.attribute(null, "pkg", p.info.provider.getPackageName());
1209 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001210 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001211 p.tag = providerIndex;
1212 providerIndex++;
1213 }
1214 }
1215
1216 N = mHosts.size();
1217 for (int i=0; i<N; i++) {
1218 Host host = mHosts.get(i);
1219 out.startTag(null, "h");
1220 out.attribute(null, "pkg", host.packageName);
1221 out.attribute(null, "id", Integer.toHexString(host.hostId));
1222 out.endTag(null, "h");
1223 host.tag = i;
1224 }
1225
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001226 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001227 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001228 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001230 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1232 if (id.provider != null) {
1233 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1234 }
1235 out.endTag(null, "g");
1236 }
1237
1238 out.endTag(null, "gs");
1239
1240 out.endDocument();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001241 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 } catch (IOException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001243 Slog.w(TAG, "Failed to write state: " + e);
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001244 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 }
1246 }
1247
Adam Cohen97300312011-10-12 15:48:13 -07001248 void readStateFromFileLocked(FileInputStream stream) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249 boolean success = false;
1250
1251 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252 XmlPullParser parser = Xml.newPullParser();
1253 parser.setInput(stream, null);
1254
1255 int type;
1256 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001257 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001258 do {
1259 type = parser.next();
1260 if (type == XmlPullParser.START_TAG) {
1261 String tag = parser.getName();
1262 if ("p".equals(tag)) {
1263 // TODO: do we need to check that this package has the same signature
1264 // as before?
1265 String pkg = parser.getAttributeValue(null, "pkg");
1266 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001267
1268 final PackageManager packageManager = mContext.getPackageManager();
1269 try {
1270 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1271 } catch (PackageManager.NameNotFoundException e) {
1272 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1273 new String[] { pkg });
1274 pkg = pkgs[0];
1275 }
1276
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1278 if (p == null && mSafeMode) {
1279 // if we're in safe mode, make a temporary one
1280 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001281 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 p.info.provider = new ComponentName(pkg, cl);
1283 p.zombie = true;
1284 mInstalledProviders.add(p);
1285 }
1286 if (p != null) {
1287 // if it wasn't uninstalled or something
1288 loadedProviders.put(providerIndex, p);
1289 }
1290 providerIndex++;
1291 }
1292 else if ("h".equals(tag)) {
1293 Host host = new Host();
1294
1295 // TODO: do we need to check that this package has the same signature
1296 // as before?
1297 host.packageName = parser.getAttributeValue(null, "pkg");
1298 try {
1299 host.uid = getUidForPackage(host.packageName);
1300 } catch (PackageManager.NameNotFoundException ex) {
1301 host.zombie = true;
1302 }
1303 if (!host.zombie || mSafeMode) {
1304 // In safe mode, we don't discard the hosts we don't recognize
1305 // so that they're not pruned from our list. Otherwise, we do.
1306 host.hostId = Integer.parseInt(
1307 parser.getAttributeValue(null, "id"), 16);
1308 mHosts.add(host);
1309 }
1310 }
1311 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001312 AppWidgetId id = new AppWidgetId();
1313 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1314 if (id.appWidgetId >= mNextAppWidgetId) {
1315 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001316 }
1317
1318 String providerString = parser.getAttributeValue(null, "p");
1319 if (providerString != null) {
1320 // there's no provider if it hasn't been bound yet.
1321 // maybe we don't have to save this, but it brings the system
1322 // to the state it was in.
1323 int pIndex = Integer.parseInt(providerString, 16);
1324 id.provider = loadedProviders.get(pIndex);
1325 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001326 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 + pIndex + " which is " + id.provider);
1328 }
1329 if (id.provider == null) {
1330 // This provider is gone. We just let the host figure out
1331 // that this happened when it fails to load it.
1332 continue;
1333 }
1334 }
1335
1336 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1337 id.host = mHosts.get(hIndex);
1338 if (id.host == null) {
1339 // This host is gone.
1340 continue;
1341 }
1342
1343 if (id.provider != null) {
1344 id.provider.instances.add(id);
1345 }
1346 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001347 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348 }
1349 }
1350 } while (type != XmlPullParser.END_DOCUMENT);
1351 success = true;
1352 } catch (NullPointerException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001353 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 } catch (NumberFormatException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001355 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 } catch (XmlPullParserException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001357 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 } catch (IOException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001359 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001360 } catch (IndexOutOfBoundsException e) {
Adam Cohen97300312011-10-12 15:48:13 -07001361 Slog.w(TAG, "failed parsing " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 }
1363
1364 if (success) {
1365 // delete any hosts that didn't manage to get connected (should happen)
1366 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001367 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 pruneHostLocked(mHosts.get(i));
1369 }
1370 } else {
1371 // failed reading, clean up
Adam Cohen97300312011-10-12 15:48:13 -07001372 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1373
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001374 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 mHosts.clear();
1376 final int N = mInstalledProviders.size();
1377 for (int i=0; i<N; i++) {
1378 mInstalledProviders.get(i).instances.clear();
1379 }
1380 }
1381 }
1382
Adam Cohen97300312011-10-12 15:48:13 -07001383 AtomicFile savedStateFile() {
1384 return new AtomicFile(new File("/data/system/" + SETTINGS_FILENAME));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 }
1386
1387 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1388 public void onReceive(Context context, Intent intent) {
1389 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001390 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1392 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001393 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1394 Locale revised = Locale.getDefault();
1395 if (revised == null || mLocale == null ||
1396 !(revised.equals(mLocale))) {
1397 mLocale = revised;
1398
1399 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001400 ensureStateLoadedLocked();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001401 int N = mInstalledProviders.size();
1402 for (int i=N-1; i>=0; i--) {
1403 Provider p = mInstalledProviders.get(i);
1404 String pkgName = p.info.provider.getPackageName();
1405 updateProvidersForPackageLocked(pkgName);
1406 }
1407 saveStateLocked();
1408 }
1409 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001411 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001412 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001413 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001414 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001415 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1416 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001417 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001418 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1419 added = false;
1420 } else {
1421 Uri uri = intent.getData();
1422 if (uri == null) {
1423 return;
1424 }
1425 String pkgName = uri.getSchemeSpecificPart();
1426 if (pkgName == null) {
1427 return;
1428 }
1429 pkgList = new String[] { pkgName };
1430 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001431 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001432 }
1433 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001434 return;
1435 }
Joe Onoratod070e892011-01-07 20:50:37 -08001436 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001437 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001438 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001439 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001440 if (changed || (extras != null &&
1441 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001442 for (String pkgName : pkgList) {
1443 // The package was just upgraded
1444 updateProvidersForPackageLocked(pkgName);
1445 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 } else {
1447 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001448 for (String pkgName : pkgList) {
1449 addProvidersForPackageLocked(pkgName);
1450 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 }
1452 saveStateLocked();
1453 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001454 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001455 Bundle extras = intent.getExtras();
1456 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1457 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1458 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001459 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001460 ensureStateLoadedLocked();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001461 for (String pkgName : pkgList) {
1462 removeProvidersForPackageLocked(pkgName);
1463 saveStateLocked();
1464 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001465 }
1466 }
1467 }
1468 }
1469 }
1470 };
1471
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001473 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001474 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1476 PackageManager.GET_META_DATA);
1477
Bjorn Bringert5f857802010-02-10 23:09:48 +00001478 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 for (int i=0; i<N; i++) {
1480 ResolveInfo ri = broadcastReceivers.get(i);
1481 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001482 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1483 continue;
1484 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 if (pkgName.equals(ai.packageName)) {
1486 addProviderLocked(ri);
1487 }
1488 }
1489 }
1490
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001492 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001493 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001494 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001495 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1496 PackageManager.GET_META_DATA);
1497
1498 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001499 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001500 for (int i=0; i<N; i++) {
1501 ResolveInfo ri = broadcastReceivers.get(i);
1502 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001503 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1504 continue;
1505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001506 if (pkgName.equals(ai.packageName)) {
1507 ComponentName component = new ComponentName(ai.packageName, ai.name);
1508 Provider p = lookupProviderLocked(component);
1509 if (p == null) {
1510 if (addProviderLocked(ri)) {
1511 keep.add(ai.name);
1512 }
1513 } else {
1514 Provider parsed = parseProviderInfoXml(component, ri);
1515 if (parsed != null) {
1516 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001517 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 p.info = parsed.info;
1519 // If it's enabled
1520 final int M = p.instances.size();
1521 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001522 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001523 // Reschedule for the new updatePeriodMillis (don't worry about handling
1524 // it specially if updatePeriodMillis didn't change because we just sent
1525 // an update, and the next one will be updatePeriodMillis from now).
1526 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001527 registerForBroadcastsLocked(p, appWidgetIds);
1528 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001529 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001530 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001531 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 if (id.host != null && id.host.callbacks != null) {
1533 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001534 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 } catch (RemoteException ex) {
1536 // It failed; remove the callback. No need to prune because
1537 // we know that this host is still referenced by this
1538 // instance.
1539 id.host.callbacks = null;
1540 }
1541 }
1542 }
1543 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001544 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 }
1546 }
1547 }
1548 }
1549 }
1550
1551 // prune the ones we don't want to keep
1552 N = mInstalledProviders.size();
1553 for (int i=N-1; i>=0; i--) {
1554 Provider p = mInstalledProviders.get(i);
1555 if (pkgName.equals(p.info.provider.getPackageName())
1556 && !keep.contains(p.info.provider.getClassName())) {
1557 removeProviderLocked(i, p);
1558 }
1559 }
1560 }
1561
1562 void removeProvidersForPackageLocked(String pkgName) {
1563 int N = mInstalledProviders.size();
1564 for (int i=N-1; i>=0; i--) {
1565 Provider p = mInstalledProviders.get(i);
1566 if (pkgName.equals(p.info.provider.getPackageName())) {
1567 removeProviderLocked(i, p);
1568 }
1569 }
1570
1571 // Delete the hosts for this package too
1572 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001573 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001574 // so we don't need to worry about sending DISABLE broadcasts to them.
1575 N = mHosts.size();
1576 for (int i=N-1; i>=0; i--) {
1577 Host host = mHosts.get(i);
1578 if (pkgName.equals(host.packageName)) {
1579 deleteHostLocked(host);
1580 }
1581 }
1582 }
1583}
1584