AppWidgetService.java revision 20e809870d8ac1e5b848f2daf51b2272ef89bdfc
1/*
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;
21import android.appwidget.AppWidgetManager;
22import android.appwidget.AppWidgetProviderInfo;
23import android.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.ServiceConnection;
29import android.content.pm.PackageManager;
30import android.os.Bundle;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.os.UserHandle;
34import android.util.Pair;
35import android.util.Slog;
36import android.util.SparseArray;
37import android.widget.RemoteViews;
38
39import com.android.internal.appwidget.IAppWidgetHost;
40import com.android.internal.appwidget.IAppWidgetService;
41import com.android.internal.widget.IRemoteViewsAdapterConnection;
42
43import java.io.FileDescriptor;
44import java.io.PrintWriter;
45import java.util.ArrayList;
46import java.util.List;
47import java.util.Locale;
48
49
50/**
51 * Redirects calls to this service to the instance of the service for the appropriate user.
52 */
53class AppWidgetService extends IAppWidgetService.Stub
54{
55    private static final String TAG = "AppWidgetService";
56
57    /*
58     * When identifying a Host or Provider based on the calling process, use the uid field.
59     * When identifying a Host or Provider based on a package manager broadcast, use the
60     * package given.
61     */
62
63    static class Provider {
64        int uid;
65        AppWidgetProviderInfo info;
66        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
67        PendingIntent broadcast;
68        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
69
70        int tag;    // for use while saving state (the index)
71    }
72
73    static class Host {
74        int uid;
75        int hostId;
76        String packageName;
77        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
78        IAppWidgetHost callbacks;
79        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
80
81        int tag;    // for use while saving state (the index)
82    }
83
84    static class AppWidgetId {
85        int appWidgetId;
86        Provider provider;
87        RemoteViews views;
88        Host host;
89    }
90
91    /**
92     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
93     * This needs to be a static inner class since a reference to the ServiceConnection is held
94     * globally and may lead us to leak AppWidgetService instances (if there were more than one).
95     */
96    static class ServiceConnectionProxy implements ServiceConnection {
97        private final IBinder mConnectionCb;
98
99        ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
100            mConnectionCb = connectionCb;
101        }
102        public void onServiceConnected(ComponentName name, IBinder service) {
103            final IRemoteViewsAdapterConnection cb =
104                IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
105            try {
106                cb.onServiceConnected(service);
107            } catch (Exception e) {
108                e.printStackTrace();
109            }
110        }
111        public void onServiceDisconnected(ComponentName name) {
112            disconnect();
113        }
114        public void disconnect() {
115            final IRemoteViewsAdapterConnection cb =
116                IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
117            try {
118                cb.onServiceDisconnected();
119            } catch (Exception e) {
120                e.printStackTrace();
121            }
122        }
123    }
124
125    Context mContext;
126    Locale mLocale;
127    PackageManager mPackageManager;
128    AlarmManager mAlarmManager;
129    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
130    int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
131    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
132    ArrayList<Host> mHosts = new ArrayList<Host>();
133    boolean mSafeMode;
134
135
136    private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
137
138    AppWidgetService(Context context) {
139        mContext = context;
140        mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
141        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0);
142        mAppWidgetServices.append(0, primary);
143    }
144
145    public void systemReady(boolean safeMode) {
146        mSafeMode = safeMode;
147
148        mAppWidgetServices.get(0).systemReady(safeMode);
149
150        // Register for the boot completed broadcast, so we can send the
151        // ENABLE broacasts. If we try to send them now, they time out,
152        // because the system isn't ready to handle them yet.
153        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
154                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
155
156        // Register for configuration changes so we can update the names
157        // of the widgets when the locale changes.
158        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
159                Intent.ACTION_CONFIGURATION_CHANGED), null, null);
160
161        // Register for broadcasts about package install, etc., so we can
162        // update the provider list.
163        IntentFilter filter = new IntentFilter();
164        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
165        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
166        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
167        filter.addDataScheme("package");
168        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
169                filter, null, null);
170        // Register for events related to sdcard installation.
171        IntentFilter sdFilter = new IntentFilter();
172        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
173        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
174        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
175                sdFilter, null, null);
176
177        IntentFilter userFilter = new IntentFilter();
178        userFilter.addAction(Intent.ACTION_USER_REMOVED);
179        mContext.registerReceiver(new BroadcastReceiver() {
180            @Override
181            public void onReceive(Context context, Intent intent) {
182                onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
183            }
184        }, userFilter);
185
186        IntentFilter userStopFilter = new IntentFilter();
187        userStopFilter.addAction(Intent.ACTION_USER_STOPPED);
188        mContext.registerReceiverAsUser(new BroadcastReceiver() {
189            @Override
190            public void onReceive(Context context, Intent intent) {
191                onUserStopped(getSendingUserId());
192            }
193        }, UserHandle.ALL, userFilter, null, null);
194    }
195
196    @Override
197    public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
198        return getImplForUser(UserHandle.getCallingUserId()).allocateAppWidgetId(
199                packageName, hostId);
200    }
201
202    @Override
203    public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
204        getImplForUser(UserHandle.getCallingUserId()).deleteAppWidgetId(appWidgetId);
205    }
206
207    @Override
208    public void deleteHost(int hostId) throws RemoteException {
209        getImplForUser(UserHandle.getCallingUserId()).deleteHost(hostId);
210    }
211
212    @Override
213    public void deleteAllHosts() throws RemoteException {
214        getImplForUser(UserHandle.getCallingUserId()).deleteAllHosts();
215    }
216
217    @Override
218    public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
219        getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetId(appWidgetId, provider);
220    }
221
222    @Override
223    public boolean bindAppWidgetIdIfAllowed(
224            String packageName, int appWidgetId, ComponentName provider) throws RemoteException {
225        return getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetIdIfAllowed(
226                packageName, appWidgetId, provider);
227    }
228
229    @Override
230    public boolean hasBindAppWidgetPermission(String packageName) throws RemoteException {
231        return getImplForUser(UserHandle.getCallingUserId()).hasBindAppWidgetPermission(
232                packageName);
233    }
234
235    @Override
236    public void setBindAppWidgetPermission(String packageName, boolean permission)
237            throws RemoteException {
238        getImplForUser(UserHandle.getCallingUserId()).setBindAppWidgetPermission(
239                packageName, permission);
240    }
241
242    @Override
243    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
244            throws RemoteException {
245        getImplForUser(UserHandle.getCallingUserId()).bindRemoteViewsService(
246                appWidgetId, intent, connection);
247    }
248
249    @Override
250    public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
251            List<RemoteViews> updatedViews) throws RemoteException {
252        return getImplForUser(UserHandle.getCallingUserId()).startListening(host,
253                packageName, hostId, updatedViews);
254    }
255
256    public void onUserRemoved(int userId) {
257        AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
258        if (userId < 1) return;
259
260        if (impl == null) {
261            AppWidgetServiceImpl.getSettingsFile(userId).delete();
262        } else {
263            impl.onUserRemoved();
264        }
265    }
266
267    public void onUserStopped(int userId) {
268    }
269
270    private AppWidgetServiceImpl getImplForUser(int userId) {
271        AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
272        if (service == null) {
273            Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
274            // TODO: Verify that it's a valid user
275            service = new AppWidgetServiceImpl(mContext, userId);
276            service.systemReady(mSafeMode);
277            // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
278            service.sendInitialBroadcasts();
279            mAppWidgetServices.append(userId, service);
280        }
281
282        return service;
283    }
284
285    @Override
286    public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
287        return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetIds(provider);
288    }
289
290    @Override
291    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
292        return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetInfo(appWidgetId);
293    }
294
295    @Override
296    public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
297        return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetViews(appWidgetId);
298    }
299
300    @Override
301    public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
302        getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetOptions(appWidgetId, options);
303    }
304
305    @Override
306    public Bundle getAppWidgetOptions(int appWidgetId) {
307        return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetOptions(appWidgetId);
308    }
309
310    static int[] getAppWidgetIds(Provider p) {
311        int instancesSize = p.instances.size();
312        int appWidgetIds[] = new int[instancesSize];
313        for (int i=0; i<instancesSize; i++) {
314            appWidgetIds[i] = p.instances.get(i).appWidgetId;
315        }
316        return appWidgetIds;
317    }
318
319    @Override
320    public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
321        return getImplForUser(UserHandle.getCallingUserId()).getInstalledProviders();
322    }
323
324    @Override
325    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
326            throws RemoteException {
327        getImplForUser(UserHandle.getCallingUserId()).notifyAppWidgetViewDataChanged(
328                appWidgetIds, viewId);
329    }
330
331    @Override
332    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
333            throws RemoteException {
334        getImplForUser(UserHandle.getCallingUserId()).partiallyUpdateAppWidgetIds(
335                appWidgetIds, views);
336    }
337
338    @Override
339    public void stopListening(int hostId) throws RemoteException {
340        getImplForUser(UserHandle.getCallingUserId()).stopListening(hostId);
341    }
342
343    @Override
344    public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
345        getImplForUser(UserHandle.getCallingUserId()).unbindRemoteViewsService(
346                appWidgetId, intent);
347    }
348
349    @Override
350    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
351        getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetIds(appWidgetIds, views);
352    }
353
354    @Override
355    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
356            throws RemoteException {
357        getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetProvider(provider, views);
358    }
359
360    @Override
361    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
362        // Dump the state of all the app widget providers
363        for (int i = 0; i < mAppWidgetServices.size(); i++) {
364            AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
365            service.dump(fd, pw, args);
366        }
367    }
368
369    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
370        public void onReceive(Context context, Intent intent) {
371            String action = intent.getAction();
372            // Slog.d(TAG, "received " + action);
373            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
374                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
375                if (userId >= 0) {
376                    getImplForUser(userId).sendInitialBroadcasts();
377                } else {
378                    Slog.w(TAG, "Not user handle supplied in " + intent);
379                }
380            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
381                for (int i = 0; i < mAppWidgetServices.size(); i++) {
382                    AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
383                    service.onConfigurationChanged();
384                }
385            } else {
386                int sendingUser = getSendingUserId();
387                if (sendingUser == UserHandle.USER_ALL) {
388                    for (int i = 0; i < mAppWidgetServices.size(); i++) {
389                        AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
390                        service.onBroadcastReceived(intent);
391                    }
392                } else {
393                    AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser);
394                    if (service != null) {
395                        service.onBroadcastReceived(intent);
396                    }
397                }
398            }
399        }
400    };
401}
402