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

Commit dfdccacd authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 4380782 from 106aeb32 to oc-mr1-release

Change-Id: I8695ba6d16804ded44468328a1d7d01554d9a1a0
parents dfbe7af5 106aeb32
Loading
Loading
Loading
Loading
+3 −4
Original line number Original line Diff line number Diff line
@@ -2583,22 +2583,21 @@ public class ExifInterface {
                            ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
                            ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
                }
                }


                // Note that the rotation angle from MediaMetadataRetriever for heif images
                // are CCW, while rotation in ExifInterface orientations are CW.
                String rotation = retriever.extractMetadata(
                String rotation = retriever.extractMetadata(
                        MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
                        MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
                if (rotation != null) {
                if (rotation != null) {
                    int orientation = ExifInterface.ORIENTATION_NORMAL;
                    int orientation = ExifInterface.ORIENTATION_NORMAL;


                    // all rotation angles in CW
                    switch (Integer.parseInt(rotation)) {
                    switch (Integer.parseInt(rotation)) {
                        case 90:
                        case 90:
                            orientation = ExifInterface.ORIENTATION_ROTATE_270;
                            orientation = ExifInterface.ORIENTATION_ROTATE_90;
                            break;
                            break;
                        case 180:
                        case 180:
                            orientation = ExifInterface.ORIENTATION_ROTATE_180;
                            orientation = ExifInterface.ORIENTATION_ROTATE_180;
                            break;
                            break;
                        case 270:
                        case 270:
                            orientation = ExifInterface.ORIENTATION_ROTATE_90;
                            orientation = ExifInterface.ORIENTATION_ROTATE_270;
                            break;
                            break;
                    }
                    }


+265 −58
Original line number Original line Diff line number Diff line
@@ -22,14 +22,18 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.IBinder.DeathRecipient;
import android.os.IInterface;
import android.os.IInterface;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserHandle;
import android.util.Slog;
import android.util.Slog;


import java.text.SimpleDateFormat;
import java.util.Objects;
import java.util.Objects;
import java.util.Date;


/**
/**
 * Manages the lifecycle of an application-provided service bound from system server.
 * Manages the lifecycle of an application-provided service bound from system server.
@@ -39,6 +43,40 @@ import java.util.Objects;
public class ManagedApplicationService {
public class ManagedApplicationService {
    private final String TAG = getClass().getSimpleName();
    private final String TAG = getClass().getSimpleName();


    /**
     * Attempt to reconnect service forever if an onBindingDied or onServiceDisconnected event
     * is received.
     */
    public static final int RETRY_FOREVER = 1;

    /**
     * Never attempt to reconnect the service - a single onBindingDied or onServiceDisconnected
     * event will cause this to fully unbind the service and never attempt to reconnect.
     */
    public static final int RETRY_NEVER = 2;

    /**
     * Attempt to reconnect the service until the maximum number of retries is reached, then stop.
     *
     * The first retry will occur MIN_RETRY_DURATION_MS after the disconnection, and each
     * subsequent retry will occur after 2x the duration used for the previous retry up to the
     * MAX_RETRY_DURATION_MS duration.
     *
     * In this case, retries mean a full unbindService/bindService pair to handle cases when the
     * usual service re-connection logic in ActiveServices has very high backoff times or when the
     * serviceconnection has fully died due to a package update or similar.
     */
    public static final int RETRY_BEST_EFFORT = 3;

    // Maximum number of retries before giving up (for RETRY_BEST_EFFORT).
    private static final int MAX_RETRY_COUNT = 4;
    // Max time between retry attempts.
    private static final long MAX_RETRY_DURATION_MS = 16000;
    // Min time between retry attempts.
    private static final long MIN_RETRY_DURATION_MS = 2000;
    // Time since the last retry attempt after which to clear the retry attempt counter.
    private static final long RETRY_RESET_TIME_MS = MAX_RETRY_DURATION_MS * 4;

    private final Context mContext;
    private final Context mContext;
    private final int mUserId;
    private final int mUserId;
    private final ComponentName mComponent;
    private final ComponentName mComponent;
@@ -46,27 +84,75 @@ public class ManagedApplicationService {
    private final String mSettingsAction;
    private final String mSettingsAction;
    private final BinderChecker mChecker;
    private final BinderChecker mChecker;
    private final boolean mIsImportant;
    private final boolean mIsImportant;

    private final int mRetryType;
    private final DeathRecipient mDeathRecipient = new DeathRecipient() {
    private final Handler mHandler;
        @Override
    private final Runnable mRetryRunnable = this::doRetry;
        public void binderDied() {
    private final EventCallback mEventCb;
            synchronized (mLock) {
                mBoundInterface = null;
            }
        }
    };


    private final Object mLock = new Object();
    private final Object mLock = new Object();


    // State protected by mLock
    // State protected by mLock
    private ServiceConnection mPendingConnection;
    private ServiceConnection mConnection;
    private ServiceConnection mConnection;
    private IInterface mBoundInterface;
    private IInterface mBoundInterface;
    private PendingEvent mPendingEvent;
    private PendingEvent mPendingEvent;
    private int mRetryCount;
    private long mLastRetryTimeMs;
    private long mNextRetryDurationMs = MIN_RETRY_DURATION_MS;
    private boolean mRetrying;

    public static interface LogFormattable {
       String toLogString(SimpleDateFormat dateFormat);
    }

    /**
     * Lifecycle event of this managed service.
     */
    public static class LogEvent implements LogFormattable {
        public static final int EVENT_CONNECTED = 1;
        public static final int EVENT_DISCONNECTED = 2;
        public static final int EVENT_BINDING_DIED = 3;
        public static final int EVENT_STOPPED_PERMANENTLY = 4;

        // Time of the events in "current time ms" timebase.
        public final long timestamp;
        // Name of the component for this system service.
        public final ComponentName component;
        // ID of the event that occurred.
        public final int event;

        public LogEvent(long timestamp, ComponentName component, int event) {
            this.timestamp = timestamp;
            this.component = component;
            this.event = event;
        }

        @Override
        public String toLogString(SimpleDateFormat dateFormat) {
            return dateFormat.format(new Date(timestamp)) + "   " + eventToString(event)
                    + " Managed Service: "
                    + ((component == null) ? "None" : component.flattenToString());
        }

        public static String eventToString(int event) {
            switch (event) {
                case EVENT_CONNECTED:
                    return "Connected";
                case EVENT_DISCONNECTED:
                    return "Disconnected";
                case EVENT_BINDING_DIED:
                    return "Binding Died For";
                case EVENT_STOPPED_PERMANENTLY:
                    return "Permanently Stopped";
                default:
                    return "Unknown Event Occurred";
            }
        }
    }


    private ManagedApplicationService(final Context context, final ComponentName component,
    private ManagedApplicationService(final Context context, final ComponentName component,
            final int userId, int clientLabel, String settingsAction,
            final int userId, int clientLabel, String settingsAction,
            BinderChecker binderChecker, boolean isImportant) {
            BinderChecker binderChecker, boolean isImportant, int retryType, Handler handler,
            EventCallback eventCallback) {
        mContext = context;
        mContext = context;
        mComponent = component;
        mComponent = component;
        mUserId = userId;
        mUserId = userId;
@@ -74,6 +160,9 @@ public class ManagedApplicationService {
        mSettingsAction = settingsAction;
        mSettingsAction = settingsAction;
        mChecker = binderChecker;
        mChecker = binderChecker;
        mIsImportant = isImportant;
        mIsImportant = isImportant;
        mRetryType = retryType;
        mHandler = handler;
        mEventCb = eventCallback;
    }
    }


    /**
    /**
@@ -91,6 +180,16 @@ public class ManagedApplicationService {
        void runEvent(IInterface service) throws RemoteException;
        void runEvent(IInterface service) throws RemoteException;
    }
    }


    /**
     * Implement to be notified about any problems with remote service.
     */
    public interface EventCallback {
        /**
         * Called when an sevice lifecycle event occurs.
         */
        void onServiceEvent(LogEvent event);
    }

    /**
    /**
     * Create a new ManagedApplicationService object but do not yet bind to the user service.
     * Create a new ManagedApplicationService object but do not yet bind to the user service.
     *
     *
@@ -104,14 +203,19 @@ public class ManagedApplicationService {
     * @param binderChecker an interface used to validate the returned binder object, or null if
     * @param binderChecker an interface used to validate the returned binder object, or null if
     *      this interface is unchecked.
     *      this interface is unchecked.
     * @param isImportant bind the user service with BIND_IMPORTANT.
     * @param isImportant bind the user service with BIND_IMPORTANT.
     * @param retryType reconnect behavior to have when bound service is disconnected.
     * @param handler the Handler to use for retries and delivering EventCallbacks.
     * @param eventCallback a callback used to deliver disconnection events, or null if you
     *      don't care.
     * @return a ManagedApplicationService instance.
     * @return a ManagedApplicationService instance.
     */
     */
    public static ManagedApplicationService build(@NonNull final Context context,
    public static ManagedApplicationService build(@NonNull final Context context,
            @NonNull final ComponentName component, final int userId, int clientLabel,
            @NonNull final ComponentName component, final int userId, int clientLabel,
            @Nullable String settingsAction, @Nullable BinderChecker binderChecker,
            @Nullable String settingsAction, @Nullable BinderChecker binderChecker,
            boolean isImportant) {
            boolean isImportant, int retryType, @NonNull Handler handler,
            @Nullable EventCallback eventCallback) {
        return new ManagedApplicationService(context, component, userId, clientLabel,
        return new ManagedApplicationService(context, component, userId, clientLabel,
            settingsAction, binderChecker, isImportant);
            settingsAction, binderChecker, isImportant, retryType, handler, eventCallback);
    }
    }




@@ -145,7 +249,6 @@ public class ManagedApplicationService {
        return true;
        return true;
    }
    }



    /**
    /**
     * Send an event to run as soon as the binder interface is available.
     * Send an event to run as soon as the binder interface is available.
     *
     *
@@ -174,15 +277,13 @@ public class ManagedApplicationService {
     */
     */
    public void disconnect() {
    public void disconnect() {
        synchronized (mLock) {
        synchronized (mLock) {
            // Wipe out pending connections
            mPendingConnection = null;

            // Unbind existing connection, if it exists
            // Unbind existing connection, if it exists
            if (mConnection != null) {
            if (mConnection == null) {
                mContext.unbindService(mConnection);
                return;
                mConnection = null;
            }
            }


            mContext.unbindService(mConnection);
            mConnection = null;
            mBoundInterface = null;
            mBoundInterface = null;
        }
        }
    }
    }
@@ -192,7 +293,7 @@ public class ManagedApplicationService {
     */
     */
    public void connect() {
    public void connect() {
        synchronized (mLock) {
        synchronized (mLock) {
            if (mConnection != null || mPendingConnection != null) {
            if (mConnection != null) {
                // We're already connected or are trying to connect
                // We're already connected or are trying to connect
                return;
                return;
            }
            }
@@ -206,72 +307,101 @@ public class ManagedApplicationService {
                        PendingIntent.getActivity(mContext, 0, new Intent(mSettingsAction), 0));
                        PendingIntent.getActivity(mContext, 0, new Intent(mSettingsAction), 0));
            }
            }


            final ServiceConnection serviceConnection = new ServiceConnection() {
            mConnection = new ServiceConnection() {
                @Override
                public void onBindingDied(ComponentName componentName) {
                    final long timestamp = System.currentTimeMillis();
                    Slog.w(TAG, "Service binding died: " + componentName);
                    synchronized (mLock) {
                        if (mConnection != this) {
                            return;
                        }
                        mHandler.post(() -> {
                            mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
                                  LogEvent.EVENT_BINDING_DIED));
                        });

                        mBoundInterface = null;
                        startRetriesLocked();
                    }
                }

                @Override
                @Override
                public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                    final long timestamp = System.currentTimeMillis();
                    Slog.i(TAG, "Service connected: " + componentName);
                    IInterface iface = null;
                    IInterface iface = null;
                    PendingEvent pendingEvent = null;
                    PendingEvent pendingEvent = null;
                    synchronized (mLock) {
                    synchronized (mLock) {
                        if (mPendingConnection == this) {
                        if (mConnection != this) {
                            // No longer pending, remove from pending connection
                            // Must've been unbound.
                            mPendingConnection = null;
                            mConnection = this;
                        } else {
                            // Service connection wasn't pending, must have been disconnected
                            mContext.unbindService(this);
                            return;
                            return;
                        }
                        }
                        mHandler.post(() -> {
                            mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
                                  LogEvent.EVENT_CONNECTED));
                        });

                        stopRetriesLocked();


                        try {
                            iBinder.linkToDeath(mDeathRecipient, 0);
                        mBoundInterface = null;
                        mBoundInterface = null;
                        if (mChecker != null) {
                        if (mChecker != null) {
                            mBoundInterface = mChecker.asInterface(iBinder);
                            mBoundInterface = mChecker.asInterface(iBinder);
                            if (!mChecker.checkType(mBoundInterface)) {
                            if (!mChecker.checkType(mBoundInterface)) {
                                    // Received an invalid binder, disconnect
                                // Received an invalid binder, disconnect.
                                    mContext.unbindService(this);
                                mBoundInterface = null;
                                mBoundInterface = null;
                                Slog.w(TAG, "Invalid binder from " + componentName);
                                startRetriesLocked();
                                return;
                            }
                            }
                            iface = mBoundInterface;
                            iface = mBoundInterface;
                            pendingEvent = mPendingEvent;
                            pendingEvent = mPendingEvent;
                            mPendingEvent = null;
                            mPendingEvent = null;
                        }
                        }
                        } catch (RemoteException e) {
                            // DOA
                            Slog.w(TAG, "Unable to bind service: " + componentName, e);
                            mBoundInterface = null;
                        }
                    }
                    }
                    if (iface != null && pendingEvent != null) {
                    if (iface != null && pendingEvent != null) {
                        try {
                        try {
                            pendingEvent.runEvent(iface);
                            pendingEvent.runEvent(iface);
                        } catch (RuntimeException | RemoteException ex) {
                        } catch (RuntimeException | RemoteException ex) {
                            Slog.e(TAG, "Received exception from user service: ", ex);
                            Slog.e(TAG, "Received exception from user service: ", ex);
                            startRetriesLocked();
                        }
                        }
                    }
                    }
                }
                }


                @Override
                @Override
                public void onServiceDisconnected(ComponentName componentName) {
                public void onServiceDisconnected(ComponentName componentName) {
                    final long timestamp = System.currentTimeMillis();
                    Slog.w(TAG, "Service disconnected: " + componentName);
                    Slog.w(TAG, "Service disconnected: " + componentName);
                    mConnection = null;
                    synchronized (mLock) {
                        if (mConnection != this) {
                            return;
                        }

                        mHandler.post(() -> {
                            mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
                                  LogEvent.EVENT_DISCONNECTED));
                        });

                        mBoundInterface = null;
                        mBoundInterface = null;
                        startRetriesLocked();
                    }
                }
                }
            };
            };


            mPendingConnection = serviceConnection;

            int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
            int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
            if (mIsImportant) {
            if (mIsImportant) {
                flags |= Context.BIND_IMPORTANT;
                flags |= Context.BIND_IMPORTANT;
            }
            }
            try {
            try {
                if (!mContext.bindServiceAsUser(intent, serviceConnection, flags,
                if (!mContext.bindServiceAsUser(intent, mConnection, flags,
                        new UserHandle(mUserId))) {
                        new UserHandle(mUserId))) {
                    Slog.w(TAG, "Unable to bind service: " + intent);
                    Slog.w(TAG, "Unable to bind service: " + intent);
                    startRetriesLocked();
                }
                }
            } catch (SecurityException e) {
            } catch (SecurityException e) {
                Slog.w(TAG, "Unable to bind service: " + intent, e);
                Slog.w(TAG, "Unable to bind service: " + intent, e);
                startRetriesLocked();
            }
            }
        }
        }
    }
    }
@@ -279,4 +409,81 @@ public class ManagedApplicationService {
    private boolean matches(final ComponentName component, final int userId) {
    private boolean matches(final ComponentName component, final int userId) {
        return Objects.equals(mComponent, component) && mUserId == userId;
        return Objects.equals(mComponent, component) && mUserId == userId;
    }
    }

    private void startRetriesLocked() {
        if (checkAndDeliverServiceDiedCbLocked()) {
            // If we delivered the service callback, disconnect and stop retrying.
            disconnect();
            return;
        }

        if (mRetrying) {
            // Retry already queued, don't queue a new one.
            return;
        }
        mRetrying = true;
        queueRetryLocked();
    }

    private void stopRetriesLocked() {
        mRetrying = false;
        mHandler.removeCallbacks(mRetryRunnable);
    }

    private void queueRetryLocked() {
        long now = SystemClock.uptimeMillis();
        if ((now - mLastRetryTimeMs) > RETRY_RESET_TIME_MS) {
            // It's been longer than the reset time since we last had to retry.  Re-initialize.
            mNextRetryDurationMs = MIN_RETRY_DURATION_MS;
            mRetryCount = 0;
        }
        mLastRetryTimeMs = now;
        mHandler.postDelayed(mRetryRunnable, mNextRetryDurationMs);
        mNextRetryDurationMs = Math.min(2 * mNextRetryDurationMs, MAX_RETRY_DURATION_MS);
        mRetryCount++;
    }

    private boolean checkAndDeliverServiceDiedCbLocked() {

       if (mRetryType == RETRY_NEVER || (mRetryType == RETRY_BEST_EFFORT
                && mRetryCount >= MAX_RETRY_COUNT)) {
            // If we never retry, or we've exhausted our retries, post the onServiceDied callback.
            Slog.e(TAG, "Service " + mComponent + " has died too much, not retrying.");
            if (mEventCb != null) {
                final long timestamp = System.currentTimeMillis();
                mHandler.post(() -> {
                  mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
                        LogEvent.EVENT_STOPPED_PERMANENTLY));
                });
            }
            return true;
        }
        return false;
    }

    private void doRetry() {
        synchronized (mLock) {
            if (mConnection == null) {
                // We disconnected for good.  Don't attempt to retry.
                return;
            }
            if (!mRetrying) {
                // We successfully connected.  Don't attempt to retry.
                return;
            }
            Slog.i(TAG, "Attempting to reconnect " + mComponent + "...");
            // While frameworks may restart the remote Service if we stay bound, we have little
            // control of the backoff timing for reconnecting the service.  In the event of a
            // process crash, the backoff time can be very large (1-30 min), which is not
            // acceptable for the types of services this is used for.  Instead force an unbind/bind
            // sequence to cause a more immediate retry.
            disconnect();
            if (checkAndDeliverServiceDiedCbLocked()) {
                // No more retries.
                return;
            }
            queueRetryLocked();
            connect();
        }
    }
}
}
+135 −47

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1

File changed.

Contains only whitespace changes.