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

Commit 892f371c authored by Jeff Brown's avatar Jeff Brown Committed by Android (Google) Code Review
Browse files

Merge "Encapsulate locks in UEventObservers." into jb-mr1-dev

parents 4b72463d 008b1762
Loading
Loading
Loading
Loading
+112 −84
Original line number Diff line number Diff line
@@ -37,14 +37,79 @@ import java.util.HashMap;
 * @hide
*/
public abstract class UEventObserver {
    private static final String TAG = UEventObserver.class.getSimpleName();
    private static UEventThread sThread;

    private static native void native_setup();
    private static native int next_event(byte[] buffer);

    public UEventObserver() {
    }

    protected void finalize() throws Throwable {
        try {
            stopObserving();
        } finally {
            super.finalize();
        }
    }

    private static UEventThread getThread() {
        synchronized (UEventObserver.class) {
            if (sThread == null) {
                sThread = new UEventThread();
                sThread.start();
            }
            return sThread;
        }
    }

    private static UEventThread peekThread() {
        synchronized (UEventObserver.class) {
            return sThread;
        }
    }

    /**
     * Begin observation of UEvent's.<p>
     * This method will cause the UEvent thread to start if this is the first
     * invocation of startObserving in this process.<p>
     * Once called, the UEvent thread will call onUEvent() when an incoming
     * UEvent matches the specified string.<p>
     * This method can be called multiple times to register multiple matches.
     * Only one call to stopObserving is required even with multiple registered
     * matches.
     * @param match A substring of the UEvent to match. Use "" to match all
     *              UEvent's
     */
    public final void startObserving(String match) {
        final UEventThread t = getThread();
        t.addObserver(match, this);
    }

    /**
     * End observation of UEvent's.<p>
     * This process's UEvent thread will never call onUEvent() on this
     * UEventObserver after this call. Repeated calls have no effect.
     */
    public final void stopObserving() {
        final UEventThread t = getThread();
        if (t != null) {
            t.removeObserver(this);
        }
    }

    /**
     * Subclasses of UEventObserver should override this method to handle
     * UEvents.
     */
    public abstract void onUEvent(UEvent event);

    /**
     * Representation of a UEvent.
     */
    static public class UEvent {
    public static final class UEvent {
        // collection of key=value pairs parsed from the uevent message
        public HashMap<String,String> mMap = new HashMap<String,String>();
        private final HashMap<String,String> mMap = new HashMap<String,String>();

        public UEvent(String message) {
            int offset = 0;
@@ -79,17 +144,17 @@ public abstract class UEventObserver {
        }
    }

    private static UEventThread sThread;
    private static boolean sThreadStarted = false;

    private static class UEventThread extends Thread {
    private static final class UEventThread extends Thread {
        /** Many to many mapping of string match to observer.
         *  Multimap would be better, but not available in android, so use
         *  an ArrayList where even elements are the String match and odd
         *  elements the corresponding UEventObserver observer */
        private ArrayList<Object> mObservers = new ArrayList<Object>();
        private final ArrayList<Object> mKeysAndObservers = new ArrayList<Object>();

        private final ArrayList<UEventObserver> mTempObserversToSignal =
                new ArrayList<UEventObserver>();

        UEventThread() {
        public UEventThread() {
            super("UEventObserver");
        }

@@ -101,91 +166,54 @@ public abstract class UEventObserver {
            while (true) {
                len = next_event(buffer);
                if (len > 0) {
                    String bufferStr = new String(buffer, 0, len);  // easier to search a String
                    synchronized (mObservers) {
                        for (int i = 0; i < mObservers.size(); i += 2) {
                            if (bufferStr.indexOf((String)mObservers.get(i)) != -1) {
                                ((UEventObserver)mObservers.get(i+1))
                                        .onUEvent(new UEvent(bufferStr));
                            }
                        }
                    sendEvent(new String(buffer, 0, len));
                }
            }
        }
        }
        public void addObserver(String match, UEventObserver observer) {
            synchronized(mObservers) {
                mObservers.add(match);
                mObservers.add(observer);
            }
        }
        /** Removes every key/value pair where value=observer from mObservers */
        public void removeObserver(UEventObserver observer) {
            synchronized(mObservers) {
                boolean found = true;
                while (found) {
                    found = false;
                    for (int i = 0; i < mObservers.size(); i += 2) {
                        if (mObservers.get(i+1) == observer) {
                            mObservers.remove(i+1);
                            mObservers.remove(i);
                            found = true;
                            break;

        private void sendEvent(String message) {
            synchronized (mKeysAndObservers) {
                final int N = mKeysAndObservers.size();
                for (int i = 0; i < N; i += 2) {
                    final String key = (String)mKeysAndObservers.get(i);
                    if (message.indexOf(key) != -1) {
                        final UEventObserver observer =
                                (UEventObserver)mKeysAndObservers.get(i + 1);
                        mTempObserversToSignal.add(observer);
                    }
                }
            }

            if (!mTempObserversToSignal.isEmpty()) {
                final UEvent event = new UEvent(message);
                final int N = mTempObserversToSignal.size();
                for (int i = 0; i < N; i++) {
                    final UEventObserver observer = mTempObserversToSignal.get(i);
                    observer.onUEvent(event);
                }
                mTempObserversToSignal.clear();
            }
        }

    private static native void native_setup();
    private static native int next_event(byte[] buffer);

    private static final synchronized void ensureThreadStarted() {
        if (sThreadStarted == false) {
            sThread = new UEventThread();
            sThread.start();
            sThreadStarted = true;
        public void addObserver(String match, UEventObserver observer) {
            synchronized (mKeysAndObservers) {
                mKeysAndObservers.add(match);
                mKeysAndObservers.add(observer);
            }
        }

    /**
     * Begin observation of UEvent's.<p>
     * This method will cause the UEvent thread to start if this is the first
     * invocation of startObserving in this process.<p>
     * Once called, the UEvent thread will call onUEvent() when an incoming
     * UEvent matches the specified string.<p>
     * This method can be called multiple times to register multiple matches.
     * Only one call to stopObserving is required even with multiple registered
     * matches.
     * @param match A substring of the UEvent to match. Use "" to match all
     *              UEvent's
     */
    public final synchronized void startObserving(String match) {
        ensureThreadStarted();
        sThread.addObserver(match, this);
        /** Removes every key/value pair where value=observer from mObservers */
        public void removeObserver(UEventObserver observer) {
            synchronized (mKeysAndObservers) {
                for (int i = 0; i < mKeysAndObservers.size(); ) {
                    if (mKeysAndObservers.get(i + 1) == observer) {
                        mKeysAndObservers.remove(i + 1);
                        mKeysAndObservers.remove(i);
                    } else {
                        i += 2;
                    }
                }

    /**
     * End observation of UEvent's.<p>
     * This process's UEvent thread will never call onUEvent() on this
     * UEventObserver after this call. Repeated calls have no effect.
     */
    public final synchronized void stopObserving() {
        sThread.removeObserver(this);
            }

    /**
     * Subclasses of UEventObserver should override this method to handle
     * UEvents.
     */
    public abstract void onUEvent(UEvent event);

    protected void finalize() throws Throwable {
        try {
            stopObserving();
        } finally {
            super.finalize();
        }
    }
}
+121 −112
Original line number Diff line number Diff line
@@ -18,10 +18,6 @@ package com.android.server;

import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK;

import com.android.server.power.PowerManagerService;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +26,7 @@ import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -47,7 +44,7 @@ import java.io.FileReader;
/**
 * <p>DockObserver monitors for a docking station.
 */
class DockObserver extends UEventObserver {
final class DockObserver extends UEventObserver {
    private static final String TAG = DockObserver.class.getSimpleName();
    private static final boolean LOG = false;

@@ -56,7 +53,9 @@ class DockObserver extends UEventObserver {

    private static final int DEFAULT_DOCK = 1;

    private static final int MSG_DOCK_STATE = 0;
    private static final int MSG_DOCK_STATE_CHANGED = 0;

    private final Object mLock = new Object();

    private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
    private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -78,7 +77,7 @@ class DockObserver extends UEventObserver {
            Slog.v(TAG, "Dock UEVENT: " + event.toString());
        }

        synchronized (this) {
        synchronized (mLock) {
            try {
                int newState = Integer.parseInt(event.get("SWITCH_STATE"));
                if (newState != mDockState) {
@@ -96,7 +95,7 @@ class DockObserver extends UEventObserver {
                                    (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
                            pm.wakeUp(SystemClock.uptimeMillis());
                        }
                        update();
                        updateLocked();
                    }
                }
            } catch (NumberFormatException e) {
@@ -105,46 +104,42 @@ class DockObserver extends UEventObserver {
        }
    }

    private final void init() {
        char[] buffer = new char[1024];

    private void init() {
        synchronized (mLock) {
            try {
                char[] buffer = new char[1024];
                FileReader file = new FileReader(DOCK_STATE_PATH);
                try {
                    int len = file.read(buffer, 0, 1024);
                    mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
                    mPreviousDockState = mDockState;
                } finally {
                    file.close();
            mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
                }
            } catch (FileNotFoundException e) {
                Slog.w(TAG, "This kernel does not have dock station support");
            } catch (Exception e) {
                Slog.e(TAG, "" , e);
            }
        }
    }

    void systemReady() {
        synchronized (this) {
        synchronized (mLock) {
            // don't bother broadcasting undocked here
            if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
                update();
                updateLocked();
            }
            mSystemReady = true;
        }
    }

    private final void update() {
        mHandler.sendEmptyMessage(MSG_DOCK_STATE);
    }

    private static boolean isScreenSaverActivatedOnDock(Context context) {
        return 0 != Settings.Secure.getInt(
                    context.getContentResolver(), SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_DOCK);
    private void updateLocked() {
        mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED);
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DOCK_STATE:
                    synchronized (this) {
    private void handleDockStateChange() {
        synchronized (mLock) {
            Slog.i(TAG, "Dock state changed: " + mDockState);

            final ContentResolver cr = mContext.getContentResolver();
@@ -154,6 +149,7 @@ class DockObserver extends UEventObserver {
                Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
                return;
            }

            // Pack up the values and broadcast them to everyone
            Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -161,16 +157,16 @@ class DockObserver extends UEventObserver {

            // Check if this is Bluetooth Dock
            // TODO(BT): Get Dock address.
                        String address = null;
                        if (address != null)
                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE,
                                    BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address));
            // String address = null;
            // if (address != null) {
            //    intent.putExtra(BluetoothDevice.EXTRA_DEVICE,
            //            BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address));
            // }

            // User feedback to confirm dock connection. Particularly
            // useful for flaky contact pins...
            if (Settings.System.getInt(cr,
                                Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1)
                        {
                    Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1) {
                String whichSound = null;
                if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
                    if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
@@ -231,6 +227,19 @@ class DockObserver extends UEventObserver {
                mContext.sendStickyBroadcast(intent);
            }
        }
    }

    private static boolean isScreenSaverActivatedOnDock(Context context) {
        return Settings.Secure.getInt(context.getContentResolver(),
                SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_DOCK) != 0;
    }

    private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DOCK_STATE_CHANGED:
                    handleDockStateChange();
                    break;
            }
        }
+153 −152

File changed.

Preview size limit exceeded, changes collapsed.