blob: 9f4936df40b230d99a63e72c76464a797fa73a37 [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
Dianne Hackborn2f0b1752011-05-31 17:59:49 -070032import org.apache.commons.logging.impl.SimpleLog;
Winson Chung81f39eb2011-01-11 18:05:01 -080033import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35import org.xmlpull.v1.XmlSerializer;
36
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.app.AlarmManager;
38import android.app.PendingIntent;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070039import android.appwidget.AppWidgetManager;
40import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.content.BroadcastReceiver;
42import android.content.ComponentName;
43import android.content.Context;
44import android.content.Intent;
Winson Chung81f39eb2011-01-11 18:05:01 -080045import android.content.Intent.FilterComparison;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.content.IntentFilter;
Winson Chung81f39eb2011-01-11 18:05:01 -080047import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.content.pm.ActivityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -040049import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.content.pm.PackageInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080051import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.content.pm.ResolveInfo;
Winson Chung81f39eb2011-01-11 18:05:01 -080053import android.content.pm.ServiceInfo;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -080054import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.content.res.TypedArray;
56import android.content.res.XmlResourceParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.net.Uri;
58import android.os.Binder;
59import android.os.Bundle;
Winson Chung81f39eb2011-01-11 18:05:01 -080060import android.os.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 Chung16c8d8a2011-01-20 16:19:33 -080065import android.util.Log;
Winson Chung81f39eb2011-01-11 18:05:01 -080066import android.util.Pair;
Joe Onorato8a9b2202010-02-26 18:56:32 -080067import android.util.Slog;
Mitsuru Oshima8f25c422009-07-01 00:10:43 -070068import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.util.Xml;
70import android.widget.RemoteViews;
Winson Chung84bbb022011-02-21 13:57:45 -080071import android.widget.RemoteViewsService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070073import com.android.internal.appwidget.IAppWidgetHost;
Winson Chung81f39eb2011-01-11 18:05:01 -080074import com.android.internal.appwidget.IAppWidgetService;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080075import com.android.internal.util.FastXmlSerializer;
Winson Chung81f39eb2011-01-11 18:05:01 -080076import com.android.internal.widget.IRemoteViewsAdapterConnection;
Winson Chung84bbb022011-02-21 13:57:45 -080077import com.android.internal.widget.IRemoteViewsFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070079class AppWidgetService extends IAppWidgetService.Stub
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080{
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070081 private static final String TAG = "AppWidgetService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070083 private static final String SETTINGS_FILENAME = "appwidgets.xml";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
Joe Onoratobe96b3a2009-07-14 19:49:27 -070085 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
87 /*
88 * When identifying a Host or Provider based on the calling process, use the uid field.
89 * When identifying a Host or Provider based on a package manager broadcast, use the
90 * package given.
91 */
92
93 static class Provider {
94 int uid;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070095 AppWidgetProviderInfo info;
Romain Guya5475592009-07-01 17:20:08 -070096 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 PendingIntent broadcast;
98 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
99
100 int tag; // for use while saving state (the index)
101 }
102
103 static class Host {
104 int uid;
105 int hostId;
106 String packageName;
Romain Guya5475592009-07-01 17:20:08 -0700107 ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700108 IAppWidgetHost callbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
110
111 int tag; // for use while saving state (the index)
112 }
113
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700114 static class AppWidgetId {
115 int appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 Provider provider;
117 RemoteViews views;
118 Host host;
119 }
120
Winson Chung81f39eb2011-01-11 18:05:01 -0800121 /**
122 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
123 * This needs to be a static inner class since a reference to the ServiceConnection is held
124 * globally and may lead us to leak AppWidgetService instances (if there were more than one).
125 */
126 static class ServiceConnectionProxy implements ServiceConnection {
Winson Chung81f39eb2011-01-11 18:05:01 -0800127 private final Pair<Integer, Intent.FilterComparison> mKey;
128 private final IBinder mConnectionCb;
129
Winson Chung16c8d8a2011-01-20 16:19:33 -0800130 ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800131 mKey = key;
132 mConnectionCb = connectionCb;
133 }
134 public void onServiceConnected(ComponentName name, IBinder service) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800135 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800136 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
137 try {
138 cb.onServiceConnected(service);
Adam Cohenc2be22c2011-03-16 16:33:53 -0700139 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800140 e.printStackTrace();
141 }
142 }
143 public void onServiceDisconnected(ComponentName name) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800144 disconnect();
145 }
146 public void disconnect() {
147 final IRemoteViewsAdapterConnection cb =
Winson Chung81f39eb2011-01-11 18:05:01 -0800148 IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
149 try {
150 cb.onServiceDisconnected();
Adam Cohenc2be22c2011-03-16 16:33:53 -0700151 } catch (Exception e) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800152 e.printStackTrace();
153 }
154 }
155 }
156
Winson Chung84bbb022011-02-21 13:57:45 -0800157 // Manages active connections to RemoteViewsServices
Winson Chung81f39eb2011-01-11 18:05:01 -0800158 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
159 mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
Winson Chung84bbb022011-02-21 13:57:45 -0800160 // Manages persistent references to RemoteViewsServices from different App Widgets
161 private final HashMap<FilterComparison, HashSet<Integer>>
162 mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
Winson Chung81f39eb2011-01-11 18:05:01 -0800163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 Context mContext;
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700165 Locale mLocale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 PackageManager mPackageManager;
167 AlarmManager mAlarmManager;
Romain Guya5475592009-07-01 17:20:08 -0700168 ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700169 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
Romain Guya5475592009-07-01 17:20:08 -0700170 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
171 ArrayList<Host> mHosts = new ArrayList<Host>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 boolean mSafeMode;
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700173 boolean mStateLoaded;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700175 AppWidgetService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 mContext = context;
177 mPackageManager = context.getPackageManager();
178 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
179 }
180
181 public void systemReady(boolean safeMode) {
182 mSafeMode = safeMode;
183
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700184 synchronized (mAppWidgetIds) {
185 ensureStateLoadedLocked();
186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187
188 // Register for the boot completed broadcast, so we can send the
189 // ENABLE broacasts. If we try to send them now, they time out,
190 // because the system isn't ready to handle them yet.
191 mContext.registerReceiver(mBroadcastReceiver,
192 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
193
Eric Fischer63c2d9e2009-10-22 15:22:50 -0700194 // Register for configuration changes so we can update the names
195 // of the widgets when the locale changes.
196 mContext.registerReceiver(mBroadcastReceiver,
197 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 // Register for broadcasts about package install, etc., so we can
200 // update the provider list.
201 IntentFilter filter = new IntentFilter();
202 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
Joe Onoratod070e892011-01-07 20:50:37 -0800203 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
205 filter.addDataScheme("package");
206 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800207 // Register for events related to sdcard installation.
208 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800209 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
210 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800211 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 }
213
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700214 private void ensureStateLoadedLocked() {
215 if (!mStateLoaded) {
216 loadAppWidgetList();
217 loadStateLocked();
218 mStateLoaded = true;
219 }
220 }
221
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 @Override
223 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
224 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
225 != PackageManager.PERMISSION_GRANTED) {
226 pw.println("Permission Denial: can't dump from from pid="
227 + Binder.getCallingPid()
228 + ", uid=" + Binder.getCallingUid());
229 return;
230 }
231
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700232 synchronized (mAppWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 int N = mInstalledProviders.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700234 pw.println("Providers:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 for (int i=0; i<N; i++) {
236 Provider p = mInstalledProviders.get(i);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700237 AppWidgetProviderInfo info = p.info;
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700238 pw.print(" ["); pw.print(i); pw.print("] provider ");
239 pw.print(info.provider.flattenToShortString());
240 pw.println(':');
241 pw.print(" min=("); pw.print(info.minWidth);
242 pw.print("x"); pw.print(info.minHeight);
Adam Cohen324afba2011-07-22 11:51:45 -0700243 pw.print(") minResize=("); pw.print(info.minResizeWidth);
244 pw.print("x"); pw.print(info.minResizeHeight);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700245 pw.print(") updatePeriodMillis=");
246 pw.print(info.updatePeriodMillis);
Adam Cohen324afba2011-07-22 11:51:45 -0700247 pw.print(" resizeMode=");
248 pw.print(info.resizeMode);
249 pw.print(" autoAdvanceViewId=");
250 pw.print(info.autoAdvanceViewId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700251 pw.print(" initialLayout=#");
252 pw.print(Integer.toHexString(info.initialLayout));
253 pw.print(" zombie="); pw.println(p.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 }
255
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700256 N = mAppWidgetIds.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700257 pw.println(" ");
258 pw.println("AppWidgetIds:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700260 AppWidgetId id = mAppWidgetIds.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700261 pw.print(" ["); pw.print(i); pw.print("] id=");
Romain Guya5475592009-07-01 17:20:08 -0700262 pw.println(id.appWidgetId);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700263 pw.print(" hostId=");
264 pw.print(id.host.hostId); pw.print(' ');
265 pw.print(id.host.packageName); pw.print('/');
266 pw.println(id.host.uid);
267 if (id.provider != null) {
268 pw.print(" provider=");
269 pw.println(id.provider.info.provider.flattenToShortString());
270 }
271 if (id.host != null) {
272 pw.print(" host.callbacks="); pw.println(id.host.callbacks);
273 }
274 if (id.views != null) {
275 pw.print(" views="); pw.println(id.views);
276 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 }
278
279 N = mHosts.size();
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700280 pw.println(" ");
281 pw.println("Hosts:");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 for (int i=0; i<N; i++) {
283 Host host = mHosts.get(i);
Dianne Hackborn1d442e02009-04-20 18:14:05 -0700284 pw.print(" ["); pw.print(i); pw.print("] hostId=");
285 pw.print(host.hostId); pw.print(' ');
286 pw.print(host.packageName); pw.print('/');
287 pw.print(host.uid); pw.println(':');
288 pw.print(" callbacks="); pw.println(host.callbacks);
289 pw.print(" instances.size="); pw.print(host.instances.size());
290 pw.print(" zombie="); pw.println(host.zombie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
292 }
293 }
294
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700295 public int allocateAppWidgetId(String packageName, int hostId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700297 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700298 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700299 int appWidgetId = mNextAppWidgetId++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300
301 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
302
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700303 AppWidgetId id = new AppWidgetId();
304 id.appWidgetId = appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 id.host = host;
306
307 host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700308 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309
310 saveStateLocked();
311
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700312 return appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 }
314 }
315
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700316 public void deleteAppWidgetId(int appWidgetId) {
317 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700318 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700319 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 if (id != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700321 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 saveStateLocked();
323 }
324 }
325 }
326
327 public void deleteHost(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700328 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700329 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 int callingUid = getCallingUid();
331 Host host = lookupHostLocked(callingUid, hostId);
332 if (host != null) {
333 deleteHostLocked(host);
334 saveStateLocked();
335 }
336 }
337 }
338
339 public void deleteAllHosts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700340 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700341 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 int callingUid = getCallingUid();
343 final int N = mHosts.size();
344 boolean changed = false;
345 for (int i=N-1; i>=0; i--) {
346 Host host = mHosts.get(i);
347 if (host.uid == callingUid) {
348 deleteHostLocked(host);
349 changed = true;
350 }
351 }
352 if (changed) {
353 saveStateLocked();
354 }
355 }
356 }
357
358 void deleteHostLocked(Host host) {
359 final int N = host.instances.size();
360 for (int i=N-1; i>=0; i--) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700361 AppWidgetId id = host.instances.get(i);
362 deleteAppWidgetLocked(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 }
364 host.instances.clear();
365 mHosts.remove(host);
366 // it's gone or going away, abruptly drop the callback connection
367 host.callbacks = null;
368 }
369
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700370 void deleteAppWidgetLocked(AppWidgetId id) {
Winson Chung81f39eb2011-01-11 18:05:01 -0800371 // We first unbind all services that are bound to this id
372 unbindAppWidgetRemoteViewsServicesLocked(id);
373
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 Host host = id.host;
375 host.instances.remove(id);
376 pruneHostLocked(host);
377
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700378 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379
380 Provider p = id.provider;
381 if (p != null) {
382 p.instances.remove(id);
383 if (!p.zombie) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700384 // send the broacast saying that this appWidgetId has been deleted
385 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 intent.setComponent(p.info.provider);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700387 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 mContext.sendBroadcast(intent);
389 if (p.instances.size() == 0) {
390 // cancel the future updates
391 cancelBroadcasts(p);
392
393 // send the broacast saying that the provider is not in use any more
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700394 intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 intent.setComponent(p.info.provider);
396 mContext.sendBroadcast(intent);
397 }
398 }
399 }
400 }
401
402 void cancelBroadcasts(Provider p) {
403 if (p.broadcast != null) {
404 mAlarmManager.cancel(p.broadcast);
405 long token = Binder.clearCallingIdentity();
406 try {
407 p.broadcast.cancel();
408 } finally {
409 Binder.restoreCallingIdentity(token);
410 }
411 p.broadcast = null;
412 }
413 }
414
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700415 public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
416 mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
417 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
Dianne Hackborn4912f162011-06-15 17:40:44 -0700418
419 final long ident = Binder.clearCallingIdentity();
420 try {
421 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700422 ensureStateLoadedLocked();
Dianne Hackborn4912f162011-06-15 17:40:44 -0700423 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
424 if (id == null) {
425 throw new IllegalArgumentException("bad appWidgetId");
426 }
427 if (id.provider != null) {
428 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
429 + id.provider.info.provider);
430 }
431 Provider p = lookupProviderLocked(provider);
432 if (p == null) {
433 throw new IllegalArgumentException("not a appwidget provider: " + provider);
434 }
435 if (p.zombie) {
436 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
437 + " safe mode: " + provider);
438 }
439
440 id.provider = p;
441 p.instances.add(id);
442 int instancesSize = p.instances.size();
443 if (instancesSize == 1) {
444 // tell the provider that it's ready
445 sendEnableIntentLocked(p);
446 }
447
448 // send an update now -- We need this update now, and just for this appWidgetId.
449 // It's less critical when the next one happens, so when we schdule the next one,
450 // we add updatePeriodMillis to its start time. That time will have some slop,
451 // but that's okay.
452 sendUpdateIntentLocked(p, new int[] { appWidgetId });
453
454 // schedule the future updates
455 registerForBroadcastsLocked(p, getAppWidgetIds(p));
456 saveStateLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 }
Dianne Hackborn4912f162011-06-15 17:40:44 -0700458 } finally {
459 Binder.restoreCallingIdentity(ident);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 }
461 }
462
Winson Chung84bbb022011-02-21 13:57:45 -0800463 // Binds to a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800464 public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
465 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700466 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800467 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
468 if (id == null) {
469 throw new IllegalArgumentException("bad appWidgetId");
470 }
471 final ComponentName componentName = intent.getComponent();
472 try {
473 final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
474 PackageManager.GET_PERMISSIONS);
475 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
476 throw new SecurityException("Selected service does not require "
477 + android.Manifest.permission.BIND_REMOTEVIEWS
478 + ": " + componentName);
479 }
480 } catch (PackageManager.NameNotFoundException e) {
481 throw new IllegalArgumentException("Unknown component " + componentName);
482 }
483
Winson Chung16c8d8a2011-01-20 16:19:33 -0800484 // If there is already a connection made for this service intent, then disconnect from
485 // that first. (This does not allow multiple connections to the same service under
486 // the same key)
487 ServiceConnectionProxy conn = null;
Winson Chung84bbb022011-02-21 13:57:45 -0800488 FilterComparison fc = new FilterComparison(intent);
489 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800490 if (mBoundRemoteViewsServices.containsKey(key)) {
491 conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
492 conn.disconnect();
493 mContext.unbindService(conn);
494 mBoundRemoteViewsServices.remove(key);
495 }
496
497 // Bind to the RemoteViewsService (which will trigger a callback to the
498 // RemoteViewsAdapter.onServiceConnected())
Winson Chung81f39eb2011-01-11 18:05:01 -0800499 final long token = Binder.clearCallingIdentity();
500 try {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800501 conn = new ServiceConnectionProxy(key, connection);
Winson Chung81f39eb2011-01-11 18:05:01 -0800502 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
503 mBoundRemoteViewsServices.put(key, conn);
504 } finally {
505 Binder.restoreCallingIdentity(token);
506 }
Winson Chung84bbb022011-02-21 13:57:45 -0800507
508 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
509 // when we can call back to the RemoteViewsService later to destroy associated
510 // factories.
Winson Chung22bc69d2011-02-25 15:13:38 -0800511 incrementAppWidgetServiceRefCount(appWidgetId, fc);
Winson Chung81f39eb2011-01-11 18:05:01 -0800512 }
513 }
514
Winson Chung84bbb022011-02-21 13:57:45 -0800515 // Unbinds from a specific RemoteViewsService
Winson Chung81f39eb2011-01-11 18:05:01 -0800516 public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
517 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700518 ensureStateLoadedLocked();
Winson Chung81f39eb2011-01-11 18:05:01 -0800519 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
520 // RemoteViewsAdapter)
521 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
522 new FilterComparison(intent));
523 if (mBoundRemoteViewsServices.containsKey(key)) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800524 // We don't need to use the appWidgetId until after we are sure there is something
525 // to unbind. Note that this may mask certain issues with apps calling unbind()
526 // more than necessary.
527 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
528 if (id == null) {
529 throw new IllegalArgumentException("bad appWidgetId");
530 }
531
532 ServiceConnectionProxy conn =
533 (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
534 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800535 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800536 mBoundRemoteViewsServices.remove(key);
537 } else {
538 Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
Winson Chung81f39eb2011-01-11 18:05:01 -0800539 }
540 }
541 }
542
Winson Chung84bbb022011-02-21 13:57:45 -0800543 // Unbinds from a RemoteViewsService when we delete an app widget
Winson Chung81f39eb2011-01-11 18:05:01 -0800544 private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800545 int appWidgetId = id.appWidgetId;
546 // Unbind all connections to Services bound to this AppWidgetId
Winson Chung81f39eb2011-01-11 18:05:01 -0800547 Iterator<Pair<Integer, Intent.FilterComparison>> it =
548 mBoundRemoteViewsServices.keySet().iterator();
Winson Chung81f39eb2011-01-11 18:05:01 -0800549 while (it.hasNext()) {
550 final Pair<Integer, Intent.FilterComparison> key = it.next();
551 if (key.first.intValue() == appWidgetId) {
Winson Chung16c8d8a2011-01-20 16:19:33 -0800552 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
553 mBoundRemoteViewsServices.get(key);
554 conn.disconnect();
Winson Chung81f39eb2011-01-11 18:05:01 -0800555 mContext.unbindService(conn);
Winson Chung16c8d8a2011-01-20 16:19:33 -0800556 it.remove();
Winson Chung81f39eb2011-01-11 18:05:01 -0800557 }
558 }
Winson Chung84bbb022011-02-21 13:57:45 -0800559
560 // Check if we need to destroy any services (if no other app widgets are
561 // referencing the same service)
562 decrementAppWidgetServiceRefCount(appWidgetId);
563 }
564
565 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
566 private void destroyRemoteViewsService(final Intent intent) {
567 final ServiceConnection conn = new ServiceConnection() {
568 @Override
569 public void onServiceConnected(ComponentName name, IBinder service) {
570 final IRemoteViewsFactory cb =
571 IRemoteViewsFactory.Stub.asInterface(service);
572 try {
573 cb.onDestroy(intent);
Adam Cohen2625fea2011-03-23 17:24:30 -0700574 } catch (RemoteException e) {
575 e.printStackTrace();
576 } catch (RuntimeException e) {
Winson Chung84bbb022011-02-21 13:57:45 -0800577 e.printStackTrace();
578 }
579 mContext.unbindService(this);
580 }
581 @Override
582 public void onServiceDisconnected(android.content.ComponentName name) {
583 // Do nothing
584 }
585 };
586
587 // Bind to the service and remove the static intent->factory mapping in the
588 // RemoteViewsService.
589 final long token = Binder.clearCallingIdentity();
590 try {
591 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
592 } finally {
593 Binder.restoreCallingIdentity(token);
594 }
595 }
596
597 // Adds to the ref-count for a given RemoteViewsService intent
598 private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
599 HashSet<Integer> appWidgetIds = null;
600 if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
601 appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
602 } else {
603 appWidgetIds = new HashSet<Integer>();
604 mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
605 }
606 appWidgetIds.add(appWidgetId);
607 }
608
609 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
610 // the ref-count reaches zero.
611 private void decrementAppWidgetServiceRefCount(int appWidgetId) {
612 Iterator<FilterComparison> it =
613 mRemoteViewsServicesAppWidgets.keySet().iterator();
614 while (it.hasNext()) {
615 final FilterComparison key = it.next();
616 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
617 if (ids.remove(appWidgetId)) {
618 // If we have removed the last app widget referencing this service, then we
619 // should destroy it and remove it from this set
620 if (ids.isEmpty()) {
621 destroyRemoteViewsService(key.getIntent());
622 it.remove();
623 }
624 }
625 }
Winson Chung81f39eb2011-01-11 18:05:01 -0800626 }
627
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700628 public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
629 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700630 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700631 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 if (id != null && id.provider != null && !id.provider.zombie) {
633 return id.provider.info;
634 }
635 return null;
636 }
637 }
638
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700639 public RemoteViews getAppWidgetViews(int appWidgetId) {
640 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700641 ensureStateLoadedLocked();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700642 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 if (id != null) {
644 return id.views;
645 }
646 return null;
647 }
648 }
649
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700650 public List<AppWidgetProviderInfo> getInstalledProviders() {
651 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700652 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 final int N = mInstalledProviders.size();
Romain Guya5475592009-07-01 17:20:08 -0700654 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 for (int i=0; i<N; i++) {
656 Provider p = mInstalledProviders.get(i);
657 if (!p.zombie) {
658 result.add(p.info);
659 }
660 }
661 return result;
662 }
663 }
664
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700665 public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
666 if (appWidgetIds == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 return;
668 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700669 if (appWidgetIds.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 return;
671 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700672 final int N = appWidgetIds.length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700674 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700675 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700677 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
678 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 }
680 }
681 }
682
Adam Cohen2dd21972010-08-15 18:20:04 -0700683 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
684 if (appWidgetIds == null) {
685 return;
686 }
687 if (appWidgetIds.length == 0) {
688 return;
689 }
690 final int N = appWidgetIds.length;
691
692 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700693 ensureStateLoadedLocked();
Adam Cohen2dd21972010-08-15 18:20:04 -0700694 for (int i=0; i<N; i++) {
695 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
696 updateAppWidgetInstanceLocked(id, views, true);
697 }
698 }
699 }
700
Winson Chung6394c0e2010-08-16 10:14:56 -0700701 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700702 if (appWidgetIds == null) {
703 return;
704 }
705 if (appWidgetIds.length == 0) {
706 return;
707 }
708 final int N = appWidgetIds.length;
709
710 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700711 ensureStateLoadedLocked();
Winson Chung499cb9f2010-07-16 11:18:17 -0700712 for (int i=0; i<N; i++) {
713 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
Winson Chung6394c0e2010-08-16 10:14:56 -0700714 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700715 }
716 }
717 }
718
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700719 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
720 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700721 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 Provider p = lookupProviderLocked(provider);
723 if (p == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800724 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 return;
726 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700727 ArrayList<AppWidgetId> instances = p.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 final int N = instances.size();
729 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700730 AppWidgetId id = instances.get(i);
731 updateAppWidgetInstanceLocked(id, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 }
733 }
734 }
735
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700736 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700737 updateAppWidgetInstanceLocked(id, views, false);
738 }
739
740 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700741 // allow for stale appWidgetIds and other badness
742 // lookup also checks that the calling process can access the appWidgetId
743 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Adam Cohen2dd21972010-08-15 18:20:04 -0700745
746 // We do not want to save this RemoteViews
747 if (!isPartialUpdate) id.views = views;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800748
749 // is anyone listening?
750 if (id.host.callbacks != null) {
751 try {
752 // the lock is held, but this is a oneway call
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700753 id.host.callbacks.updateAppWidget(id.appWidgetId, views);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 } catch (RemoteException e) {
755 // It failed; remove the callback. No need to prune because
756 // we know that this host is still referenced by this instance.
757 id.host.callbacks = null;
758 }
759 }
760 }
761 }
762
Winson Chung6394c0e2010-08-16 10:14:56 -0700763 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700764 // allow for stale appWidgetIds and other badness
765 // lookup also checks that the calling process can access the appWidgetId
766 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
767 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
Winson Chung499cb9f2010-07-16 11:18:17 -0700768 // is anyone listening?
769 if (id.host.callbacks != null) {
770 try {
771 // the lock is held, but this is a oneway call
Winson Chung6394c0e2010-08-16 10:14:56 -0700772 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
Winson Chung499cb9f2010-07-16 11:18:17 -0700773 } catch (RemoteException e) {
774 // It failed; remove the callback. No need to prune because
775 // we know that this host is still referenced by this instance.
776 id.host.callbacks = null;
777 }
778 }
779 }
780 }
781
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700782 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 List<RemoteViews> updatedViews) {
784 int callingUid = enforceCallingUid(packageName);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700785 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700786 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
788 host.callbacks = callbacks;
789
790 updatedViews.clear();
791
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700792 ArrayList<AppWidgetId> instances = host.instances;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 int N = instances.size();
794 int[] updatedIds = new int[N];
795 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700796 AppWidgetId id = instances.get(i);
797 updatedIds[i] = id.appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 updatedViews.add(id.views);
799 }
800 return updatedIds;
801 }
802 }
803
804 public void stopListening(int hostId) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700805 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700806 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 Host host = lookupHostLocked(getCallingUid(), hostId);
Ken Shirriffe21167a2009-09-23 16:42:53 -0700808 if (host != null) {
809 host.callbacks = null;
810 pruneHostLocked(host);
811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 }
813 }
814
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700815 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 if (id.host.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700817 // Apps hosting the AppWidget have access to it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 return true;
819 }
820 if (id.provider != null && id.provider.uid == callingUid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700821 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 return true;
823 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700824 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 == PackageManager.PERMISSION_GRANTED) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700826 // Apps that can bind have access to all appWidgetIds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 return true;
828 }
829 // Nobody else can access it.
830 return false;
831 }
832
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700833 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 int callingUid = getCallingUid();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700835 final int N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700837 AppWidgetId id = mAppWidgetIds.get(i);
838 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 return id;
840 }
841 }
842 return null;
843 }
844
845 Provider lookupProviderLocked(ComponentName provider) {
846 final int N = mInstalledProviders.size();
847 for (int i=0; i<N; i++) {
848 Provider p = mInstalledProviders.get(i);
Adam Cohenbac26a122011-08-16 20:42:30 -0700849 if (p.info.provider.equals(provider)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 return p;
851 }
852 }
853 return null;
854 }
855
856 Host lookupHostLocked(int uid, int hostId) {
857 final int N = mHosts.size();
858 for (int i=0; i<N; i++) {
859 Host h = mHosts.get(i);
860 if (h.uid == uid && h.hostId == hostId) {
861 return h;
862 }
863 }
864 return null;
865 }
866
867 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
868 final int N = mHosts.size();
869 for (int i=0; i<N; i++) {
870 Host h = mHosts.get(i);
871 if (h.hostId == hostId && h.packageName.equals(packageName)) {
872 return h;
873 }
874 }
875 Host host = new Host();
876 host.packageName = packageName;
877 host.uid = uid;
878 host.hostId = hostId;
879 mHosts.add(host);
880 return host;
881 }
882
883 void pruneHostLocked(Host host) {
884 if (host.instances.size() == 0 && host.callbacks == null) {
885 mHosts.remove(host);
886 }
887 }
888
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700889 void loadAppWidgetList() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 PackageManager pm = mPackageManager;
891
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700892 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
894 PackageManager.GET_META_DATA);
895
Bjorn Bringert5f857802010-02-10 23:09:48 +0000896 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 for (int i=0; i<N; i++) {
898 ResolveInfo ri = broadcastReceivers.get(i);
899 addProviderLocked(ri);
900 }
901 }
902
903 boolean addProviderLocked(ResolveInfo ri) {
Joe Onoratod070e892011-01-07 20:50:37 -0800904 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
905 return false;
906 }
907 if (!ri.activityInfo.isEnabled()) {
908 return false;
909 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
911 ri.activityInfo.name), ri);
912 if (p != null) {
913 mInstalledProviders.add(p);
914 return true;
915 } else {
916 return false;
917 }
918 }
919
920 void removeProviderLocked(int index, Provider p) {
921 int N = p.instances.size();
922 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700923 AppWidgetId id = p.instances.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 // Call back with empty RemoteViews
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700925 updateAppWidgetInstanceLocked(id, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 // Stop telling the host about updates for this from now on
927 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700928 // clear out references to this appWidgetId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 id.host.instances.remove(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700930 mAppWidgetIds.remove(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 id.provider = null;
932 pruneHostLocked(id.host);
933 id.host = null;
934 }
935 p.instances.clear();
936 mInstalledProviders.remove(index);
937 // no need to send the DISABLE broadcast, since the receiver is gone anyway
938 cancelBroadcasts(p);
939 }
940
941 void sendEnableIntentLocked(Provider p) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700942 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 intent.setComponent(p.info.provider);
944 mContext.sendBroadcast(intent);
945 }
946
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700947 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
948 if (appWidgetIds != null && appWidgetIds.length > 0) {
949 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
950 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 intent.setComponent(p.info.provider);
952 mContext.sendBroadcast(intent);
953 }
954 }
955
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700956 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 if (p.info.updatePeriodMillis > 0) {
958 // if this is the first instance, set the alarm. otherwise,
959 // rely on the fact that we've already set it and that
960 // PendingIntent.getBroadcast will update the extras.
961 boolean alreadyRegistered = p.broadcast != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700962 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
963 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 intent.setComponent(p.info.provider);
965 long token = Binder.clearCallingIdentity();
966 try {
967 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
968 PendingIntent.FLAG_UPDATE_CURRENT);
969 } finally {
970 Binder.restoreCallingIdentity(token);
971 }
972 if (!alreadyRegistered) {
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700973 long period = p.info.updatePeriodMillis;
974 if (period < MIN_UPDATE_PERIOD) {
975 period = MIN_UPDATE_PERIOD;
976 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Joe Onoratobe96b3a2009-07-14 19:49:27 -0700978 SystemClock.elapsedRealtime() + period, period, p.broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 }
980 }
981 }
982
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700983 static int[] getAppWidgetIds(Provider p) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 int instancesSize = p.instances.size();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700985 int appWidgetIds[] = new int[instancesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 for (int i=0; i<instancesSize; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700987 appWidgetIds[i] = p.instances.get(i).appWidgetId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700989 return appWidgetIds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 }
991
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700992 public int[] getAppWidgetIds(ComponentName provider) {
993 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -0700994 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 Provider p = lookupProviderLocked(provider);
996 if (p != null && getCallingUid() == p.uid) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700997 return getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 } else {
999 return new int[0];
1000 }
1001 }
1002 }
1003
1004 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1005 Provider p = null;
1006
1007 ActivityInfo activityInfo = ri.activityInfo;
1008 XmlResourceParser parser = null;
1009 try {
1010 parser = activityInfo.loadXmlMetaData(mPackageManager,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001011 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 if (parser == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001013 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001014 + "AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 return null;
1016 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001017
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 AttributeSet attrs = Xml.asAttributeSet(parser);
Adam Cohend2e20de2011-02-25 12:03:37 -08001019
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 int type;
1021 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1022 && type != XmlPullParser.START_TAG) {
1023 // drain whitespace, comments, etc.
1024 }
Adam Cohend2e20de2011-02-25 12:03:37 -08001025
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 String nodeName = parser.getName();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001027 if (!"appwidget-provider".equals(nodeName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001028 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001029 + " AppWidget provider '" + component + '\'');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 return null;
1031 }
1032
1033 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001034 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 info.provider = component;
1036 p.uid = activityInfo.applicationInfo.uid;
1037
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001038 Resources res = mPackageManager.getResourcesForApplication(
1039 activityInfo.applicationInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001040
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08001041 TypedArray sa = res.obtainAttributes(attrs,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001042 com.android.internal.R.styleable.AppWidgetProviderInfo);
Adam Cohend2e20de2011-02-25 12:03:37 -08001043
Mitsuru Oshima8f25c422009-07-01 00:10:43 -07001044 // These dimensions has to be resolved in the application's context.
1045 // We simply send back the raw complex data, which will be
1046 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1047 TypedValue value = sa.peekValue(
1048 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1049 info.minWidth = value != null ? value.data : 0;
1050 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1051 info.minHeight = value != null ? value.data : 0;
Adam Cohen1bfaf562011-07-19 18:05:33 -07001052 value = sa.peekValue(
1053 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1054 info.minResizeWidth = value != null ? value.data : info.minWidth;
1055 value = sa.peekValue(
1056 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1057 info.minResizeHeight = value != null ? value.data : info.minHeight;
Adam Cohend2e20de2011-02-25 12:03:37 -08001058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 info.updatePeriodMillis = sa.getInt(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001060 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 info.initialLayout = sa.getResourceId(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001062 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 String className = sa.getString(
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001064 com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 if (className != null) {
1066 info.configure = new ComponentName(component.getPackageName(), className);
1067 }
1068 info.label = activityInfo.loadLabel(mPackageManager).toString();
1069 info.icon = ri.getIconResource();
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001070 info.previewImage = sa.getResourceId(
1071 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
Adam Cohena02fdf12010-11-03 13:27:40 -07001072 info.autoAdvanceViewId = sa.getResourceId(
1073 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
Adam Cohen9611f2e2011-02-28 13:39:38 -08001074 info.resizeMode = sa.getInt(
Adam Cohend2e20de2011-02-25 12:03:37 -08001075 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1076 AppWidgetProviderInfo.RESIZE_NONE);
Patrick Dubroyd2db2a52010-06-23 14:56:28 -07001077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 sa.recycle();
1079 } catch (Exception e) {
1080 // Ok to catch Exception here, because anything going wrong because
1081 // of what a client process passes to us should not be fatal for the
1082 // system process.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001083 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 return null;
1085 } finally {
1086 if (parser != null) parser.close();
1087 }
1088 return p;
1089 }
1090
1091 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1092 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
1093 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1094 throw new PackageManager.NameNotFoundException();
1095 }
1096 return pkgInfo.applicationInfo.uid;
1097 }
1098
1099 int enforceCallingUid(String packageName) throws IllegalArgumentException {
1100 int callingUid = getCallingUid();
1101 int packageUid;
1102 try {
1103 packageUid = getUidForPackage(packageName);
1104 } catch (PackageManager.NameNotFoundException ex) {
1105 throw new IllegalArgumentException("packageName and uid don't match packageName="
1106 + packageName);
1107 }
Jeff Brown10e89712011-07-08 18:52:57 -07001108 if (callingUid != packageUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 throw new IllegalArgumentException("packageName and uid don't match packageName="
1110 + packageName);
1111 }
1112 return callingUid;
1113 }
1114
1115 void sendInitialBroadcasts() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001116 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001117 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 final int N = mInstalledProviders.size();
1119 for (int i=0; i<N; i++) {
1120 Provider p = mInstalledProviders.get(i);
1121 if (p.instances.size() > 0) {
1122 sendEnableIntentLocked(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001123 int[] appWidgetIds = getAppWidgetIds(p);
1124 sendUpdateIntentLocked(p, appWidgetIds);
1125 registerForBroadcastsLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 }
1127 }
1128 }
1129 }
1130
1131 // only call from initialization -- it assumes that the data structures are all empty
1132 void loadStateLocked() {
1133 File temp = savedStateTempFile();
1134 File real = savedStateRealFile();
1135
1136 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the
1137 // real one. if there is both a real file and a temp one, assume that the temp one isn't
1138 // fully written and delete it.
1139 if (real.exists()) {
1140 readStateFromFileLocked(real);
1141 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001142 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 temp.delete();
1144 }
1145 } else if (temp.exists()) {
1146 readStateFromFileLocked(temp);
Romain Guya5475592009-07-01 17:20:08 -07001147 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 temp.renameTo(real);
1149 }
1150 }
1151
1152 void saveStateLocked() {
1153 File temp = savedStateTempFile();
1154 File real = savedStateRealFile();
1155
1156 if (!real.exists()) {
1157 // If the real one doesn't exist, it's either because this is the first time
1158 // or because something went wrong while copying them. In this case, we can't
1159 // trust anything that's in temp. In order to have the loadState code not
1160 // use the temporary one until it's fully written, create an empty file
1161 // for real, which will we'll shortly delete.
1162 try {
Romain Guya5475592009-07-01 17:20:08 -07001163 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 real.createNewFile();
1165 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001166 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 }
1168 }
1169
1170 if (temp.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001171 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 temp.delete();
1173 }
1174
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001175 if (!writeStateToFileLocked(temp)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001176 Slog.w(TAG, "Failed to persist new settings");
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001177 return;
1178 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179
Romain Guya5475592009-07-01 17:20:08 -07001180 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 real.delete();
Romain Guya5475592009-07-01 17:20:08 -07001182 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183 temp.renameTo(real);
1184 }
1185
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001186 boolean writeStateToFileLocked(File file) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 FileOutputStream stream = null;
1188 int N;
1189
1190 try {
1191 stream = new FileOutputStream(file, false);
1192 XmlSerializer out = new FastXmlSerializer();
1193 out.setOutput(stream, "utf-8");
1194 out.startDocument(null, true);
1195
1196
1197 out.startTag(null, "gs");
1198
1199 int providerIndex = 0;
1200 N = mInstalledProviders.size();
1201 for (int i=0; i<N; i++) {
1202 Provider p = mInstalledProviders.get(i);
1203 if (p.instances.size() > 0) {
1204 out.startTag(null, "p");
1205 out.attribute(null, "pkg", p.info.provider.getPackageName());
1206 out.attribute(null, "cl", p.info.provider.getClassName());
Patrick Tsaibd742e432010-05-01 00:30:19 +08001207 out.endTag(null, "p");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 p.tag = providerIndex;
1209 providerIndex++;
1210 }
1211 }
1212
1213 N = mHosts.size();
1214 for (int i=0; i<N; i++) {
1215 Host host = mHosts.get(i);
1216 out.startTag(null, "h");
1217 out.attribute(null, "pkg", host.packageName);
1218 out.attribute(null, "id", Integer.toHexString(host.hostId));
1219 out.endTag(null, "h");
1220 host.tag = i;
1221 }
1222
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001223 N = mAppWidgetIds.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 for (int i=0; i<N; i++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001225 AppWidgetId id = mAppWidgetIds.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 out.startTag(null, "g");
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001227 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 out.attribute(null, "h", Integer.toHexString(id.host.tag));
1229 if (id.provider != null) {
1230 out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1231 }
1232 out.endTag(null, "g");
1233 }
1234
1235 out.endTag(null, "gs");
1236
1237 out.endDocument();
1238 stream.close();
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001239 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001240 } catch (IOException e) {
1241 try {
1242 if (stream != null) {
1243 stream.close();
1244 }
1245 } catch (IOException ex) {
Romain Guya5475592009-07-01 17:20:08 -07001246 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 }
1248 if (file.exists()) {
Romain Guya5475592009-07-01 17:20:08 -07001249 //noinspection ResultOfMethodCallIgnored
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 file.delete();
1251 }
Suchi Amalapurapu8550f252009-09-29 15:20:32 -07001252 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 }
1254 }
1255
1256 void readStateFromFileLocked(File file) {
1257 FileInputStream stream = null;
1258
1259 boolean success = false;
1260
1261 try {
1262 stream = new FileInputStream(file);
1263 XmlPullParser parser = Xml.newPullParser();
1264 parser.setInput(stream, null);
1265
1266 int type;
1267 int providerIndex = 0;
Romain Guya5475592009-07-01 17:20:08 -07001268 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 do {
1270 type = parser.next();
1271 if (type == XmlPullParser.START_TAG) {
1272 String tag = parser.getName();
1273 if ("p".equals(tag)) {
1274 // TODO: do we need to check that this package has the same signature
1275 // as before?
1276 String pkg = parser.getAttributeValue(null, "pkg");
1277 String cl = parser.getAttributeValue(null, "cl");
Romain Guyff3e61c62010-03-11 15:30:02 -08001278
1279 final PackageManager packageManager = mContext.getPackageManager();
1280 try {
1281 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1282 } catch (PackageManager.NameNotFoundException e) {
1283 String[] pkgs = packageManager.currentToCanonicalPackageNames(
1284 new String[] { pkg });
1285 pkg = pkgs[0];
1286 }
1287
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1289 if (p == null && mSafeMode) {
1290 // if we're in safe mode, make a temporary one
1291 p = new Provider();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001292 p.info = new AppWidgetProviderInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 p.info.provider = new ComponentName(pkg, cl);
1294 p.zombie = true;
1295 mInstalledProviders.add(p);
1296 }
1297 if (p != null) {
1298 // if it wasn't uninstalled or something
1299 loadedProviders.put(providerIndex, p);
1300 }
1301 providerIndex++;
1302 }
1303 else if ("h".equals(tag)) {
1304 Host host = new Host();
1305
1306 // TODO: do we need to check that this package has the same signature
1307 // as before?
1308 host.packageName = parser.getAttributeValue(null, "pkg");
1309 try {
1310 host.uid = getUidForPackage(host.packageName);
1311 } catch (PackageManager.NameNotFoundException ex) {
1312 host.zombie = true;
1313 }
1314 if (!host.zombie || mSafeMode) {
1315 // In safe mode, we don't discard the hosts we don't recognize
1316 // so that they're not pruned from our list. Otherwise, we do.
1317 host.hostId = Integer.parseInt(
1318 parser.getAttributeValue(null, "id"), 16);
1319 mHosts.add(host);
1320 }
1321 }
1322 else if ("g".equals(tag)) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001323 AppWidgetId id = new AppWidgetId();
1324 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1325 if (id.appWidgetId >= mNextAppWidgetId) {
1326 mNextAppWidgetId = id.appWidgetId + 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 }
1328
1329 String providerString = parser.getAttributeValue(null, "p");
1330 if (providerString != null) {
1331 // there's no provider if it hasn't been bound yet.
1332 // maybe we don't have to save this, but it brings the system
1333 // to the state it was in.
1334 int pIndex = Integer.parseInt(providerString, 16);
1335 id.provider = loadedProviders.get(pIndex);
1336 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001337 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001338 + pIndex + " which is " + id.provider);
1339 }
1340 if (id.provider == null) {
1341 // This provider is gone. We just let the host figure out
1342 // that this happened when it fails to load it.
1343 continue;
1344 }
1345 }
1346
1347 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1348 id.host = mHosts.get(hIndex);
1349 if (id.host == null) {
1350 // This host is gone.
1351 continue;
1352 }
1353
1354 if (id.provider != null) {
1355 id.provider.instances.add(id);
1356 }
1357 id.host.instances.add(id);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001358 mAppWidgetIds.add(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 }
1360 }
1361 } while (type != XmlPullParser.END_DOCUMENT);
1362 success = true;
1363 } catch (NullPointerException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001364 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001366 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 } catch (XmlPullParserException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001368 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001370 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 } catch (IndexOutOfBoundsException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001372 Slog.w(TAG, "failed parsing " + file, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 }
1374 try {
1375 if (stream != null) {
1376 stream.close();
1377 }
1378 } catch (IOException e) {
Romain Guya5475592009-07-01 17:20:08 -07001379 // Ignore
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 }
1381
1382 if (success) {
1383 // delete any hosts that didn't manage to get connected (should happen)
1384 // if it matters, they'll be reconnected.
Dianne Hackborn002716d2009-08-12 11:13:26 -07001385 for (int i=mHosts.size()-1; i>=0; i--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 pruneHostLocked(mHosts.get(i));
1387 }
1388 } else {
1389 // failed reading, clean up
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001390 mAppWidgetIds.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 mHosts.clear();
1392 final int N = mInstalledProviders.size();
1393 for (int i=0; i<N; i++) {
1394 mInstalledProviders.get(i).instances.clear();
1395 }
1396 }
1397 }
1398
1399 File savedStateTempFile() {
1400 return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1401 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1402 }
1403
1404 File savedStateRealFile() {
1405 return new File("/data/system/" + SETTINGS_FILENAME);
1406 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1407 }
1408
1409 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1410 public void onReceive(Context context, Intent intent) {
1411 String action = intent.getAction();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001412 //Slog.d(TAG, "received " + action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1414 sendInitialBroadcasts();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001415 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1416 Locale revised = Locale.getDefault();
1417 if (revised == null || mLocale == null ||
1418 !(revised.equals(mLocale))) {
1419 mLocale = revised;
1420
1421 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001422 ensureStateLoadedLocked();
Eric Fischer63c2d9e2009-10-22 15:22:50 -07001423 int N = mInstalledProviders.size();
1424 for (int i=N-1; i>=0; i--) {
1425 Provider p = mInstalledProviders.get(i);
1426 String pkgName = p.info.provider.getPackageName();
1427 updateProvidersForPackageLocked(pkgName);
1428 }
1429 saveStateLocked();
1430 }
1431 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 } else {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001433 boolean added = false;
Joe Onoratod070e892011-01-07 20:50:37 -08001434 boolean changed = false;
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001435 String pkgList[] = null;
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001436 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001437 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1438 added = true;
Joe Onorato94258cd2010-08-24 16:45:40 -04001439 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001440 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1441 added = false;
1442 } else {
1443 Uri uri = intent.getData();
1444 if (uri == null) {
1445 return;
1446 }
1447 String pkgName = uri.getSchemeSpecificPart();
1448 if (pkgName == null) {
1449 return;
1450 }
1451 pkgList = new String[] { pkgName };
1452 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
Joe Onoratod070e892011-01-07 20:50:37 -08001453 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001454 }
1455 if (pkgList == null || pkgList.length == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001456 return;
1457 }
Joe Onoratod070e892011-01-07 20:50:37 -08001458 if (added || changed) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001459 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001460 ensureStateLoadedLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 Bundle extras = intent.getExtras();
Joe Onoratod070e892011-01-07 20:50:37 -08001462 if (changed || (extras != null &&
1463 extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001464 for (String pkgName : pkgList) {
1465 // The package was just upgraded
1466 updateProvidersForPackageLocked(pkgName);
1467 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 } else {
1469 // The package was just added
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001470 for (String pkgName : pkgList) {
1471 addProvidersForPackageLocked(pkgName);
1472 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001473 }
1474 saveStateLocked();
1475 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001476 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001477 Bundle extras = intent.getExtras();
1478 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1479 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1480 } else {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001481 synchronized (mAppWidgetIds) {
Jeff Sharkey15d161f2011-09-01 21:30:56 -07001482 ensureStateLoadedLocked();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001483 for (String pkgName : pkgList) {
1484 removeProvidersForPackageLocked(pkgName);
1485 saveStateLocked();
1486 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 }
1488 }
1489 }
1490 }
1491 }
1492 };
1493
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001494 void addProvidersForPackageLocked(String pkgName) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001495 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001496 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001497 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1498 PackageManager.GET_META_DATA);
1499
Bjorn Bringert5f857802010-02-10 23:09:48 +00001500 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501 for (int i=0; i<N; i++) {
1502 ResolveInfo ri = broadcastReceivers.get(i);
1503 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001504 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1505 continue;
1506 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 if (pkgName.equals(ai.packageName)) {
1508 addProviderLocked(ri);
1509 }
1510 }
1511 }
1512
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 void updateProvidersForPackageLocked(String pkgName) {
Romain Guya5475592009-07-01 17:20:08 -07001514 HashSet<String> keep = new HashSet<String>();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001515 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
Joe Onoratof6133fe2010-02-01 18:24:46 -05001516 intent.setPackage(pkgName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1518 PackageManager.GET_META_DATA);
1519
1520 // add the missing ones and collect which ones to keep
Bjorn Bringert5f857802010-02-10 23:09:48 +00001521 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 for (int i=0; i<N; i++) {
1523 ResolveInfo ri = broadcastReceivers.get(i);
1524 ActivityInfo ai = ri.activityInfo;
Joe Onorato331fbdc72010-08-24 17:02:09 -04001525 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1526 continue;
1527 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001528 if (pkgName.equals(ai.packageName)) {
1529 ComponentName component = new ComponentName(ai.packageName, ai.name);
1530 Provider p = lookupProviderLocked(component);
1531 if (p == null) {
1532 if (addProviderLocked(ri)) {
1533 keep.add(ai.name);
1534 }
1535 } else {
1536 Provider parsed = parseProviderInfoXml(component, ri);
1537 if (parsed != null) {
1538 keep.add(ai.name);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001539 // Use the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 p.info = parsed.info;
1541 // If it's enabled
1542 final int M = p.instances.size();
1543 if (M > 0) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001544 int[] appWidgetIds = getAppWidgetIds(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 // Reschedule for the new updatePeriodMillis (don't worry about handling
1546 // it specially if updatePeriodMillis didn't change because we just sent
1547 // an update, and the next one will be updatePeriodMillis from now).
1548 cancelBroadcasts(p);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001549 registerForBroadcastsLocked(p, appWidgetIds);
1550 // If it's currently showing, call back with the new AppWidgetProviderInfo.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 for (int j=0; j<M; j++) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001552 AppWidgetId id = p.instances.get(j);
Joe Onoratoa8a8a422010-06-16 15:06:16 -04001553 id.views = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 if (id.host != null && id.host.callbacks != null) {
1555 try {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001556 id.host.callbacks.providerChanged(id.appWidgetId, p.info);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 } catch (RemoteException ex) {
1558 // It failed; remove the callback. No need to prune because
1559 // we know that this host is still referenced by this
1560 // instance.
1561 id.host.callbacks = null;
1562 }
1563 }
1564 }
1565 // Now that we've told the host, push out an update.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001566 sendUpdateIntentLocked(p, appWidgetIds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 }
1568 }
1569 }
1570 }
1571 }
1572
1573 // prune the ones we don't want to keep
1574 N = mInstalledProviders.size();
1575 for (int i=N-1; i>=0; i--) {
1576 Provider p = mInstalledProviders.get(i);
1577 if (pkgName.equals(p.info.provider.getPackageName())
1578 && !keep.contains(p.info.provider.getClassName())) {
1579 removeProviderLocked(i, p);
1580 }
1581 }
1582 }
1583
1584 void removeProvidersForPackageLocked(String pkgName) {
1585 int N = mInstalledProviders.size();
1586 for (int i=N-1; i>=0; i--) {
1587 Provider p = mInstalledProviders.get(i);
1588 if (pkgName.equals(p.info.provider.getPackageName())) {
1589 removeProviderLocked(i, p);
1590 }
1591 }
1592
1593 // Delete the hosts for this package too
1594 //
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001595 // By now, we have removed any AppWidgets that were in any hosts here,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 // so we don't need to worry about sending DISABLE broadcasts to them.
1597 N = mHosts.size();
1598 for (int i=N-1; i>=0; i--) {
1599 Host host = mHosts.get(i);
1600 if (pkgName.equals(host.packageName)) {
1601 deleteHostLocked(host);
1602 }
1603 }
1604 }
1605}
1606