Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72b04de0 authored by Amith Yamasani's avatar Amith Yamasani Committed by Android Git Automerger
Browse files

am b71ccfcf: Merge "Fix widget cross-talk between users due to Settings widget" into jb-mr1-dev

* commit 'b71ccfcf':
  Fix widget cross-talk between users due to Settings widget
parents e491ab0f b71ccfcf
Loading
Loading
Loading
Loading
+42 −109
Original line number Diff line number Diff line
@@ -17,34 +17,28 @@
package com.android.server;

import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.widget.RemoteViews;

import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.util.IndentingPrintWriter;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

@@ -56,85 +50,11 @@ class AppWidgetService extends IAppWidgetService.Stub
{
    private static final String TAG = "AppWidgetService";

    /*
     * When identifying a Host or Provider based on the calling process, use the uid field.
     * When identifying a Host or Provider based on a package manager broadcast, use the
     * package given.
     */

    static class Provider {
        int uid;
        AppWidgetProviderInfo info;
        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
        PendingIntent broadcast;
        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
        
        int tag;    // for use while saving state (the index)
    }

    static class Host {
        int uid;
        int hostId;
        String packageName;
        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
        IAppWidgetHost callbacks;
        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
        
        int tag;    // for use while saving state (the index)
    }

    static class AppWidgetId {
        int appWidgetId;
        Provider provider;
        RemoteViews views;
        Host host;
    }

    /**
     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
     * This needs to be a static inner class since a reference to the ServiceConnection is held
     * globally and may lead us to leak AppWidgetService instances (if there were more than one).
     */
    static class ServiceConnectionProxy implements ServiceConnection {
        private final IBinder mConnectionCb;

        ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
            mConnectionCb = connectionCb;
        }
        public void onServiceConnected(ComponentName name, IBinder service) {
            final IRemoteViewsAdapterConnection cb =
                IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
            try {
                cb.onServiceConnected(service);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        public void onServiceDisconnected(ComponentName name) {
            disconnect();
        }
        public void disconnect() {
            final IRemoteViewsAdapterConnection cb =
                IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
            try {
                cb.onServiceDisconnected();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    Context mContext;
    Locale mLocale;
    PackageManager mPackageManager;
    AlarmManager mAlarmManager;
    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
    int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
    ArrayList<Host> mHosts = new ArrayList<Host>();
    boolean mSafeMode;


    private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;

    AppWidgetService(Context context) {
@@ -195,9 +115,16 @@ class AppWidgetService extends IAppWidgetService.Stub
        }, UserHandle.ALL, userFilter, null, null);
    }

    /**
     * This returns the user id of the caller, if the caller is not the system process,
     * otherwise it assumes that the calls are from the lockscreen and hence are meant for the
     * current user. TODO: Instead, have lockscreen make explicit calls with userId
     */
    private int getCallingOrCurrentUserId() {
        int callingUid = Binder.getCallingUid();
        if (callingUid == android.os.Process.myUid()) {
        // Also check the PID because Settings (power control widget) also runs as System UID
        if (callingUid == android.os.Process.myUid()
                && Binder.getCallingPid() == android.os.Process.myPid()) {
            try {
                return ActivityManagerNative.getDefault().getCurrentUser().id;
            } catch (RemoteException re) {
@@ -272,8 +199,10 @@ class AppWidgetService extends IAppWidgetService.Stub
    }

    public void onUserRemoved(int userId) {
        AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
        if (userId < 1) return;
        synchronized (mAppWidgetServices) {
            AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
            mAppWidgetServices.remove(userId);
    
            if (impl == null) {
                AppWidgetServiceImpl.getSettingsFile(userId).delete();
@@ -281,22 +210,29 @@ class AppWidgetService extends IAppWidgetService.Stub
                impl.onUserRemoved();
            }
        }
    }

    public void onUserStopped(int userId) {
    }

    private AppWidgetServiceImpl getImplForUser(int userId) {
        AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
        boolean sendInitial = false;
        AppWidgetServiceImpl service;
        synchronized (mAppWidgetServices) {
            service = mAppWidgetServices.get(userId);
            if (service == null) {
            Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
                Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId + ", adding");
                // TODO: Verify that it's a valid user
                service = new AppWidgetServiceImpl(mContext, userId);
                service.systemReady(mSafeMode);
                // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
            service.sendInitialBroadcasts();
                mAppWidgetServices.append(userId, service);
                sendInitial = true;
            }
        }
        if (sendInitial) {
            service.sendInitialBroadcasts();
        }

        return service;
    }

@@ -325,15 +261,6 @@ class AppWidgetService extends IAppWidgetService.Stub
        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetOptions(appWidgetId);
    }

    static int[] getAppWidgetIds(Provider p) {
        int instancesSize = p.instances.size();
        int appWidgetIds[] = new int[instancesSize];
        for (int i=0; i<instancesSize; i++) {
            appWidgetIds[i] = p.instances.get(i).appWidgetId;
        }
        return appWidgetIds;
    }

    @Override
    public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
        return getImplForUser(getCallingOrCurrentUserId()).getInstalledProviders();
@@ -378,9 +305,15 @@ class AppWidgetService extends IAppWidgetService.Stub
    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        // Dump the state of all the app widget providers
        synchronized (mAppWidgetServices) {
            IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
            for (int i = 0; i < mAppWidgetServices.size(); i++) {
                pw.println("User: " + mAppWidgetServices.keyAt(i));
                ipw.increaseIndent();
                AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
            service.dump(fd, pw, args);
                service.dump(fd, ipw, args);
                ipw.decreaseIndent();
            }
        }
    }

+20 −7
Original line number Diff line number Diff line
@@ -87,6 +87,8 @@ class AppWidgetServiceImpl {
    private static final String SETTINGS_FILENAME = "appwidgets.xml";
    private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes

    private static boolean DBG = false;

    /*
     * When identifying a Host or Provider based on the calling process, use the uid field. When
     * identifying a Host or Provider based on a package manager broadcast, use the package given.
@@ -208,7 +210,12 @@ class AppWidgetServiceImpl {
        }
    }

    private void log(String msg) {
        Slog.i(TAG, "u=" + mUserId + ": " + msg);
    }

    void onConfigurationChanged() {
        if (DBG) log("Got onConfigurationChanged()");
        Locale revised = Locale.getDefault();
        if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
            mLocale = revised;
@@ -235,6 +242,7 @@ class AppWidgetServiceImpl {
    }

    void onBroadcastReceived(Intent intent) {
        if (DBG) log("onBroadcast " + intent);
        final String action = intent.getAction();
        boolean added = false;
        boolean changed = false;
@@ -425,7 +433,8 @@ class AppWidgetServiceImpl {
            mAppWidgetIds.add(id);

            saveStateLocked();

            if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
                    + " id=" + appWidgetId);
            return appWidgetId;
        }
    }
@@ -518,6 +527,7 @@ class AppWidgetServiceImpl {
    }

    void cancelBroadcasts(Provider p) {
        if (DBG) log("cancelBroadcasts for " + p);
        if (p.broadcast != null) {
            mAlarmManager.cancel(p.broadcast);
            long token = Binder.clearCallingIdentity();
@@ -531,6 +541,8 @@ class AppWidgetServiceImpl {
    }

    private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
        if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
                + " provider=" + provider);
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mAppWidgetIds) {
@@ -825,12 +837,14 @@ class AppWidgetServiceImpl {
    }

    public RemoteViews getAppWidgetViews(int appWidgetId) {
        if (DBG) log("getAppWidgetViews id=" + appWidgetId);
        synchronized (mAppWidgetIds) {
            ensureStateLoadedLocked();
            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
            if (id != null) {
                return cloneIfLocalBinder(id.views);
            }
            if (DBG) log("   couldn't find appwidgetid");
            return null;
        }
    }
@@ -854,7 +868,7 @@ class AppWidgetServiceImpl {
        if (appWidgetIds == null) {
            return;
        }

        if (DBG) log("updateAppWidgetIds views: " + views);
        int bitmapMemoryUsage = 0;
        if (views != null) {
            bitmapMemoryUsage = views.estimateMemoryUsage();
@@ -1280,8 +1294,8 @@ class AppWidgetServiceImpl {
            intent.setComponent(p.info.provider);
            long token = Binder.clearCallingIdentity();
            try {
                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
                        PendingIntent.FLAG_UPDATE_CURRENT);
                p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
                        PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
            } finally {
                Binder.restoreCallingIdentity(token);
            }
@@ -1353,7 +1367,7 @@ class AppWidgetServiceImpl {
            p.uid = activityInfo.applicationInfo.uid;

            Resources res = mContext.getPackageManager()
                    .getResourcesForApplication(activityInfo.applicationInfo);
                    .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);

            TypedArray sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AppWidgetProviderInfo);
@@ -1597,8 +1611,7 @@ class AppWidgetServiceImpl {

                        final IPackageManager packageManager = AppGlobals.getPackageManager();
                        try {
                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0,
                                    UserHandle.getCallingUserId());
                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
                        } catch (RemoteException e) {
                            String[] pkgs = mContext.getPackageManager()
                                    .currentToCanonicalPackageNames(new String[] { pkg });