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

Commit 8d5f244b authored by Neil Fuller's avatar Neil Fuller Committed by Android (Google) Code Review
Browse files

Merge "Post-review fixes from http://ag/12048560"

parents e1ff28ff c63778e7
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -117,7 +117,7 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector {
    }
    }


    private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) {
    private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) {
        ArraySet<TimeZoneConfigurationListener> configurationListeners;
        final ArraySet<TimeZoneConfigurationListener> configurationListeners;
        synchronized (this) {
        synchronized (this) {
            configurationListeners = new ArraySet<>(mConfigurationListeners);
            configurationListeners = new ArraySet<>(mConfigurationListeners);
        }
        }
+85 −89
Original line number Original line Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.server.timezonedetector;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.timezonedetector.ITimeZoneConfigurationListener;
import android.app.timezonedetector.ITimeZoneConfigurationListener;
import android.app.timezonedetector.ITimeZoneDetectorService;
import android.app.timezonedetector.ITimeZoneDetectorService;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
@@ -38,6 +37,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseArray;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
@@ -60,7 +60,8 @@ import java.util.Objects;
 * and making calls async, leaving the (consequently more testable) {@link TimeZoneDetectorStrategy}
 * and making calls async, leaving the (consequently more testable) {@link TimeZoneDetectorStrategy}
 * implementation to deal with the logic around time zone detection.
 * implementation to deal with the logic around time zone detection.
 */
 */
public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
        implements IBinder.DeathRecipient {


    private static final String TAG = "TimeZoneDetectorService";
    private static final String TAG = "TimeZoneDetectorService";


@@ -104,9 +105,15 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
    @NonNull
    @NonNull
    private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
    private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;


    /**
     * This sparse array acts as a map from userId to listeners running as that userId. User scoped
     * as time zone detection configuration is partially user-specific, so different users can
     * get different configuration.
     */
    @GuardedBy("mConfigurationListeners")
    @GuardedBy("mConfigurationListeners")
    @NonNull
    @NonNull
    private final ArrayList<ConfigListenerInfo> mConfigurationListeners = new ArrayList<>();
    private final SparseArray<ArrayList<ITimeZoneConfigurationListener>> mConfigurationListeners =
            new SparseArray<>();


    private static TimeZoneDetectorService create(
    private static TimeZoneDetectorService create(
            @NonNull Context context, @NonNull Handler handler,
            @NonNull Context context, @NonNull Handler handler,
@@ -188,18 +195,23 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
        Objects.requireNonNull(listener);
        Objects.requireNonNull(listener);
        int userId = UserHandle.getCallingUserId();
        int userId = UserHandle.getCallingUserId();


        ConfigListenerInfo listenerInfo = new ConfigListenerInfo(userId, listener);

        synchronized (mConfigurationListeners) {
        synchronized (mConfigurationListeners) {
            if (mConfigurationListeners.contains(listenerInfo)) {
            ArrayList<ITimeZoneConfigurationListener> listeners =
                    mConfigurationListeners.get(userId);
            if (listeners != null && listeners.contains(listener)) {
                return;
                return;
            }
            }
            try {
            try {
                // Ensure the reference to the listener is removed if the client process dies.
                if (listeners == null) {
                listenerInfo.linkToDeath();
                    listeners = new ArrayList<>(1);
                    mConfigurationListeners.put(userId, listeners);
                }

                // Ensure the reference to the listener will be removed if the client process dies.
                listener.asBinder().linkToDeath(this, 0 /* flags */);


                // Only add the listener if we can linkToDeath().
                // Only add the listener if we can linkToDeath().
                mConfigurationListeners.add(listenerInfo);
                listeners.add(listener);
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e);
                Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e);
            }
            }
@@ -213,22 +225,57 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
        int userId = UserHandle.getCallingUserId();
        int userId = UserHandle.getCallingUserId();


        synchronized (mConfigurationListeners) {
        synchronized (mConfigurationListeners) {
            ConfigListenerInfo toRemove = new ConfigListenerInfo(userId, listener);
            boolean removedListener = false;
            Iterator<ConfigListenerInfo> listenerIterator = mConfigurationListeners.iterator();
            ArrayList<ITimeZoneConfigurationListener> userListeners =
            while (listenerIterator.hasNext()) {
                    mConfigurationListeners.get(userId);
                ConfigListenerInfo currentListenerInfo = listenerIterator.next();
            if (userListeners.remove(listener)) {
                if (currentListenerInfo.equals(toRemove)) {
                    listenerIterator.remove();

                // Stop listening for the client process to die.
                // Stop listening for the client process to die.
                    try {
                listener.asBinder().unlinkToDeath(this, 0 /* flags */);
                        currentListenerInfo.unlinkToDeath();
                removedListener = true;
                    } catch (RemoteException e) {
                        Slog.e(TAG, "Unable to unlinkToDeath() for listener=" + listener, e);
            }
            }
            if (!removedListener) {
                Slog.w(TAG, "Client asked to remove listenener=" + listener
                        + ", but no listeners were removed."
                        + " mConfigurationListeners=" + mConfigurationListeners);
            }
            }
        }
        }
    }
    }

    @Override
    public void binderDied() {
        // Should not be used as binderDied(IBinder who) is overridden.
        Slog.wtf(TAG, "binderDied() called unexpectedly.");
    }

    /**
     * Called when one of the ITimeZoneConfigurationListener processes dies before calling
     * {@link #removeConfigurationListener(ITimeZoneConfigurationListener)}.
     */
    @Override
    public void binderDied(IBinder who) {
        synchronized (mConfigurationListeners) {
            boolean removedListener = false;
            final int userCount = mConfigurationListeners.size();
            for (int i = 0; i < userCount; i++) {
                ArrayList<ITimeZoneConfigurationListener> userListeners =
                        mConfigurationListeners.valueAt(i);
                Iterator<ITimeZoneConfigurationListener> userListenerIterator =
                        userListeners.iterator();
                while (userListenerIterator.hasNext()) {
                    ITimeZoneConfigurationListener userListener = userListenerIterator.next();
                    if (userListener.asBinder().equals(who)) {
                        userListenerIterator.remove();
                        removedListener = true;
                        break;
                    }
                }
            }
            if (!removedListener) {
                Slog.w(TAG, "Notified of binder death for who=" + who
                        + ", but did not remove any listeners."
                        + " mConfigurationListeners=" + mConfigurationListeners);
            }
        }
    }
    }


    void handleConfigurationChanged() {
    void handleConfigurationChanged() {
@@ -243,14 +290,24 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
        // problem.
        // problem.


        synchronized (mConfigurationListeners) {
        synchronized (mConfigurationListeners) {
            for (ConfigListenerInfo listenerInfo : mConfigurationListeners) {
            final int userCount = mConfigurationListeners.size();
            for (int userIndex = 0; userIndex < userCount; userIndex++) {
                int userId = mConfigurationListeners.keyAt(userIndex);
                TimeZoneConfiguration configuration =
                TimeZoneConfiguration configuration =
                        mTimeZoneDetectorStrategy.getConfiguration(listenerInfo.getUserId());
                        mTimeZoneDetectorStrategy.getConfiguration(userId);

                ArrayList<ITimeZoneConfigurationListener> listeners =
                        mConfigurationListeners.valueAt(userIndex);
                final int listenerCount = listeners.size();
                for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) {
                    ITimeZoneConfigurationListener listener = listeners.get(listenerIndex);
                    try {
                    try {
                    listenerInfo.getListener().onChange(configuration);
                        listener.onChange(configuration);
                    } catch (RemoteException e) {
                    } catch (RemoteException e) {
                    Slog.w(TAG, "Unable to notify listener="
                        Slog.w(TAG, "Unable to notify listener=" + listener
                            + listenerInfo + " of updated configuration=" + configuration, e);
                                + " for userId=" + userId
                                + " of updated configuration=" + configuration, e);
                    }
                }
                }
            }
            }
        }
        }
@@ -338,66 +395,5 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
        (new TimeZoneDetectorShellCommand(this)).exec(
        (new TimeZoneDetectorShellCommand(this)).exec(
                this, in, out, err, args, callback, resultReceiver);
                this, in, out, err, args, callback, resultReceiver);
    }
    }

    private class ConfigListenerInfo implements IBinder.DeathRecipient {
        private final @UserIdInt int mUserId;
        private final ITimeZoneConfigurationListener mListener;

        ConfigListenerInfo(
                @UserIdInt int userId, @NonNull ITimeZoneConfigurationListener listener) {
            this.mUserId = userId;
            this.mListener = Objects.requireNonNull(listener);
        }

        @UserIdInt int getUserId() {
            return mUserId;
        }

        ITimeZoneConfigurationListener getListener() {
            return mListener;
        }

        void linkToDeath() throws RemoteException {
            mListener.asBinder().linkToDeath(this, 0 /* flags */);
        }

        void unlinkToDeath() throws RemoteException {
            mListener.asBinder().unlinkToDeath(this, 0 /* flags */);
        }

        @Override
        public void binderDied() {
            synchronized (mConfigurationListeners) {
                Slog.i(TAG, "Configuration listener client died: " + this);
                mConfigurationListeners.remove(this);
            }
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            ConfigListenerInfo that = (ConfigListenerInfo) o;
            return mUserId == that.mUserId
                    && mListener.equals(that.mListener);
        }

        @Override
        public int hashCode() {
            return Objects.hash(mUserId, mListener);
        }

        @Override
        public String toString() {
            return "ConfigListenerInfo{"
                    + "mUserId=" + mUserId
                    + ", mListener=" + mListener
                    + '}';
        }
    }
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -170,7 +170,7 @@ public class TimeZoneDetectorServiceTest {
        ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
        ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
        try {
        try {
            mTimeZoneDetectorService.removeConfigurationListener(mockListener);
            mTimeZoneDetectorService.removeConfigurationListener(mockListener);
            fail();
            fail("Expected a SecurityException");
        } finally {
        } finally {
            verify(mMockContext).enforceCallingPermission(
            verify(mMockContext).enforceCallingPermission(
                    eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
                    eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),