Merge "Refactor locks in TunerAdapter"
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index aa5480a..4a18333 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -16,12 +16,13 @@
 
 package android.hardware.radio;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Bitmap;
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -29,28 +30,36 @@
 /**
  * Implements the RadioTuner interface by forwarding calls to radio service.
  */
-class TunerAdapter extends RadioTuner {
+final class TunerAdapter extends RadioTuner {
     private static final String TAG = "BroadcastRadio.TunerAdapter";
 
-    @NonNull private final ITuner mTuner;
-    @NonNull private final TunerCallbackAdapter mCallback;
-    private boolean mIsClosed = false;
+    private final ITuner mTuner;
+    private final TunerCallbackAdapter mCallback;
+    private final Object mLock = new Object();
 
-    private @RadioManager.Band int mBand;
+    @GuardedBy("mLock")
+    private boolean mIsClosed;
 
+    @GuardedBy("mLock")
+    @RadioManager.Band
+    private int mBand;
+
+    @GuardedBy("mLock")
     private ProgramList mLegacyListProxy;
+
+    @GuardedBy("mLock")
     private Map<String, String> mLegacyListFilter;
 
-    TunerAdapter(@NonNull ITuner tuner, @NonNull TunerCallbackAdapter callback,
+    TunerAdapter(ITuner tuner, TunerCallbackAdapter callback,
             @RadioManager.Band int band) {
-        mTuner = Objects.requireNonNull(tuner);
-        mCallback = Objects.requireNonNull(callback);
+        mTuner = Objects.requireNonNull(tuner, "Tuner cannot be null");
+        mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
         mBand = band;
     }
 
     @Override
     public void close() {
-        synchronized (mTuner) {
+        synchronized (mLock) {
             if (mIsClosed) {
                 Log.v(TAG, "Tuner is already closed");
                 return;
@@ -60,8 +69,8 @@
                 mLegacyListProxy.close();
                 mLegacyListProxy = null;
             }
-            mCallback.close();
         }
+        mCallback.close();
         try {
             mTuner.close();
         } catch (RemoteException e) {
@@ -71,16 +80,20 @@
 
     @Override
     public int setConfiguration(RadioManager.BandConfig config) {
-        if (config == null) return RadioManager.STATUS_BAD_VALUE;
+        if (config == null) {
+            return RadioManager.STATUS_BAD_VALUE;
+        }
         try {
             mTuner.setConfiguration(config);
-            mBand = config.getType();
+            synchronized (mLock) {
+                mBand = config.getType();
+            }
             return RadioManager.STATUS_OK;
         } catch (IllegalArgumentException e) {
             Log.e(TAG, "Can't set configuration", e);
             return RadioManager.STATUS_BAD_VALUE;
         } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
+            Log.e(TAG, "Service died", e);
             return RadioManager.STATUS_DEAD_OBJECT;
         }
     }
@@ -94,7 +107,7 @@
             config[0] = mTuner.getConfiguration();
             return RadioManager.STATUS_OK;
         } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
+            Log.e(TAG, "Service died", e);
             return RadioManager.STATUS_DEAD_OBJECT;
         }
     }
@@ -107,7 +120,7 @@
             Log.e(TAG, "Can't set muted", e);
             return RadioManager.STATUS_ERROR;
         } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
+            Log.e(TAG, "Service died", e);
             return RadioManager.STATUS_DEAD_OBJECT;
         }
         return RadioManager.STATUS_OK;
@@ -118,7 +131,7 @@
         try {
             return mTuner.isMuted();
         } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
+            Log.e(TAG, "Service died", e);
             return true;
         }
     }
@@ -126,12 +139,13 @@
     @Override
     public int step(int direction, boolean skipSubChannel) {
         try {
-            mTuner.step(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
+            mTuner.step(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN,
+                    skipSubChannel);
         } catch (IllegalStateException e) {
             Log.e(TAG, "Can't step", e);
             return RadioManager.STATUS_INVALID_OPERATION;
         } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
+            Log.e(TAG, "Service died", e);
             return RadioManager.STATUS_DEAD_OBJECT;
         }
         return RadioManager.STATUS_OK;
@@ -140,12 +154,13 @@
     @Override
     public int scan(int direction, boolean skipSubChannel) {
         try {
-            mTuner.scan(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
+            mTuner.scan(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN,
+                    skipSubChannel);
         } catch (IllegalStateException e) {
             Log.e(TAG, "Can't scan", e);
             return RadioManager.STATUS_INVALID_OPERATION;
         } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
+            Log.e(TAG, "Service died", e);
             return RadioManager.STATUS_DEAD_OBJECT;
         }
         return RadioManager.STATUS_OK;
@@ -154,7 +169,11 @@
     @Override
     public int tune(int channel, int subChannel) {
         try {
-            mTuner.tune(ProgramSelector.createAmFmSelector(mBand, channel, subChannel));
+            int band;
+            synchronized (mLock) {
+                band = mBand;
+            }
+            mTuner.tune(ProgramSelector.createAmFmSelector(band, channel, subChannel));
         } catch (IllegalStateException e) {
             Log.e(TAG, "Can't tune", e);
             return RadioManager.STATUS_INVALID_OPERATION;
@@ -162,18 +181,18 @@
             Log.e(TAG, "Can't tune", e);
             return RadioManager.STATUS_BAD_VALUE;
         } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
+            Log.e(TAG, "Service died", e);
             return RadioManager.STATUS_DEAD_OBJECT;
         }
         return RadioManager.STATUS_OK;
     }
 
     @Override
-    public void tune(@NonNull ProgramSelector selector) {
+    public void tune(ProgramSelector selector) {
         try {
             mTuner.tune(selector);
         } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
+            throw new RuntimeException("Service died", e);
         }
     }
 
@@ -185,7 +204,7 @@
             Log.e(TAG, "Can't cancel", e);
             return RadioManager.STATUS_INVALID_OPERATION;
         } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
+            Log.e(TAG, "Service died", e);
             return RadioManager.STATUS_DEAD_OBJECT;
         }
         return RadioManager.STATUS_OK;
@@ -196,7 +215,7 @@
         try {
             mTuner.cancelAnnouncement();
         } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
+            throw new RuntimeException("Service died", e);
         }
     }
 
@@ -217,11 +236,12 @@
     }
 
     @Override
-    public @Nullable Bitmap getMetadataImage(int id) {
+    @Nullable
+    public Bitmap getMetadataImage(int id) {
         try {
             return mTuner.getImage(id);
         } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
+            throw new RuntimeException("Service died", e);
         }
     }
 
@@ -230,66 +250,72 @@
         try {
             return mTuner.startBackgroundScan();
         } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
+            throw new RuntimeException("Service died", e);
         }
     }
 
     @Override
-    public @NonNull List<RadioManager.ProgramInfo>
+    public List<RadioManager.ProgramInfo>
             getProgramList(@Nullable Map<String, String> vendorFilter) {
-        synchronized (mTuner) {
+        synchronized (mLock) {
             if (mLegacyListProxy == null || !Objects.equals(mLegacyListFilter, vendorFilter)) {
                 Log.i(TAG, "Program list filter has changed, requesting new list");
                 mLegacyListProxy = new ProgramList();
                 mLegacyListFilter = vendorFilter;
-
                 mCallback.clearLastCompleteList();
-                mCallback.setProgramListObserver(mLegacyListProxy, () -> { });
-                try {
-                    mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
-                } catch (RemoteException ex) {
-                    throw new RuntimeException("service died", ex);
-                }
+                mCallback.setProgramListObserver(mLegacyListProxy, () -> {
+                    Log.i(TAG, "Empty closeListener in programListObserver");
+                });
             }
-
-            List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
-            if (list == null) throw new IllegalStateException("Program list is not ready yet");
-            return list;
         }
+        try {
+            mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
+        } catch (RemoteException ex) {
+            throw new RuntimeException("Service died", ex);
+        }
+
+        List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
+        if (list == null) {
+            throw new IllegalStateException("Program list is not ready yet");
+        }
+        return list;
     }
 
     @Override
-    public @Nullable ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
-        synchronized (mTuner) {
+    @Nullable
+    public ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
+        synchronized (mLock) {
             if (mLegacyListProxy != null) {
                 mLegacyListProxy.close();
                 mLegacyListProxy = null;
             }
             mLegacyListFilter = null;
-
-            ProgramList list = new ProgramList();
-            mCallback.setProgramListObserver(list, () -> {
-                try {
-                    mTuner.stopProgramListUpdates();
-                } catch (IllegalStateException ex) {
-                    // it's fine to not stop updates if tuner is already closed
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "Couldn't stop program list updates", ex);
-                }
-            });
-
-            try {
-                mTuner.startProgramListUpdates(filter);
-            } catch (UnsupportedOperationException ex) {
-                Log.i(TAG, "Program list is not supported with this hardware");
-                return null;
-            } catch (RemoteException ex) {
-                mCallback.setProgramListObserver(null, () -> { });
-                throw new RuntimeException("service died", ex);
-            }
-
-            return list;
         }
+        ProgramList list = new ProgramList();
+        mCallback.setProgramListObserver(list, () -> {
+            try {
+                mTuner.stopProgramListUpdates();
+            } catch (IllegalStateException ex) {
+                // it's fine to not stop updates if tuner is already closed
+                Log.e(TAG, "Tuner may already be closed", ex);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Couldn't stop program list updates", ex);
+            }
+        });
+
+        try {
+            mTuner.startProgramListUpdates(filter);
+        } catch (UnsupportedOperationException ex) {
+            Log.i(TAG, "Program list is not supported with this hardware");
+            return null;
+        } catch (RemoteException ex) {
+            mCallback.setProgramListObserver(null, () -> {
+                Log.i(TAG, "Empty closeListener in programListObserver");
+            });
+            throw new RuntimeException("Service died", ex);
+        }
+
+        return list;
     }
 
     @Override
@@ -315,7 +341,7 @@
         try {
             return mTuner.isConfigFlagSupported(flag);
         } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
+            throw new RuntimeException("Service died", e);
         }
     }
 
@@ -324,7 +350,7 @@
         try {
             return mTuner.isConfigFlagSet(flag);
         } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
+            throw new RuntimeException("Service died", e);
         }
     }
 
@@ -333,25 +359,26 @@
         try {
             mTuner.setConfigFlag(flag, value);
         } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
+            throw new RuntimeException("Service died", e);
         }
     }
 
     @Override
-    public @NonNull Map<String, String> setParameters(@NonNull Map<String, String> parameters) {
+    public Map<String, String> setParameters(Map<String, String> parameters) {
         try {
-            return mTuner.setParameters(Objects.requireNonNull(parameters));
+            return mTuner.setParameters(Objects.requireNonNull(parameters,
+                    "Parameters cannot be null"));
         } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
+            throw new RuntimeException("Service died", e);
         }
     }
 
     @Override
-    public @NonNull Map<String, String> getParameters(@NonNull List<String> keys) {
+    public Map<String, String> getParameters(List<String> keys) {
         try {
-            return mTuner.getParameters(Objects.requireNonNull(keys));
+            return mTuner.getParameters(Objects.requireNonNull(keys, "Keys cannot be null"));
         } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
+            throw new RuntimeException("Service died", e);
         }
     }
 
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index e3f7001..b9782a8 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -16,12 +16,13 @@
 
 package android.hardware.radio;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -29,23 +30,31 @@
 /**
  * Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
  */
-class TunerCallbackAdapter extends ITunerCallback.Stub {
+final class TunerCallbackAdapter extends ITunerCallback.Stub {
     private static final String TAG = "BroadcastRadio.TunerCallbackAdapter";
 
     private final Object mLock = new Object();
-    @NonNull private final RadioTuner.Callback mCallback;
-    @NonNull private final Handler mHandler;
+    private final RadioTuner.Callback mCallback;
+    private final Handler mHandler;
 
+    @GuardedBy("mLock")
     @Nullable ProgramList mProgramList;
 
     // cache for deprecated methods
+    @GuardedBy("mLock")
     boolean mIsAntennaConnected = true;
+
+    @GuardedBy("mLock")
     @Nullable List<RadioManager.ProgramInfo> mLastCompleteList;
-    private boolean mDelayedCompleteCallback = false;
+
+    @GuardedBy("mLock")
+    private boolean mDelayedCompleteCallback;
+
+    @GuardedBy("mLock")
     @Nullable RadioManager.ProgramInfo mCurrentProgramInfo;
 
-    TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
-        mCallback = callback;
+    TunerCallbackAdapter(RadioTuner.Callback callback, @Nullable Handler handler) {
+        mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
         if (handler == null) {
             mHandler = new Handler(Looper.getMainLooper());
         } else {
@@ -55,31 +64,39 @@
 
     void close() {
         synchronized (mLock) {
-            if (mProgramList != null) mProgramList.close();
+            if (mProgramList != null) {
+                mProgramList.close();
+            }
         }
     }
 
     void setProgramListObserver(@Nullable ProgramList programList,
-            @NonNull ProgramList.OnCloseListener closeListener) {
-        Objects.requireNonNull(closeListener);
+            ProgramList.OnCloseListener closeListener) {
+        Objects.requireNonNull(closeListener, "CloseListener cannot be null");
         synchronized (mLock) {
             if (mProgramList != null) {
                 Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
                 mProgramList.close();
             }
             mProgramList = programList;
-            if (programList == null) return;
+            if (programList == null) {
+                return;
+            }
             programList.setOnCloseListener(() -> {
                 synchronized (mLock) {
-                    if (mProgramList != programList) return;
+                    if (mProgramList != programList) {
+                        return;
+                    }
                     mProgramList = null;
                     mLastCompleteList = null;
-                    closeListener.onClose();
                 }
+                closeListener.onClose();
             });
             programList.addOnCompleteListener(() -> {
                 synchronized (mLock) {
-                    if (mProgramList != programList) return;
+                    if (mProgramList != programList) {
+                        return;
+                    }
                     mLastCompleteList = programList.toList();
                     if (mDelayedCompleteCallback) {
                         Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
@@ -109,7 +126,11 @@
     }
 
     boolean isAntennaConnected() {
-        return mIsAntennaConnected;
+        boolean isConnected;
+        synchronized (mLock) {
+            isConnected = mIsAntennaConnected;
+        }
+        return isConnected;
     }
 
     @Override
@@ -177,7 +198,9 @@
 
     @Override
     public void onAntennaState(boolean connected) {
-        mIsAntennaConnected = connected;
+        synchronized (mLock) {
+            mIsAntennaConnected = connected;
+        }
         mHandler.post(() -> mCallback.onAntennaState(connected));
     }
 
@@ -186,6 +209,7 @@
         mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
     }
 
+    @GuardedBy("mLock")
     private void sendBackgroundScanCompleteLocked() {
         mDelayedCompleteCallback = false;
         mHandler.post(() -> mCallback.onBackgroundScanComplete());
@@ -213,8 +237,10 @@
     public void onProgramListUpdated(ProgramList.Chunk chunk) {
         mHandler.post(() -> {
             synchronized (mLock) {
-                if (mProgramList == null) return;
-                mProgramList.apply(Objects.requireNonNull(chunk));
+                if (mProgramList == null) {
+                    return;
+                }
+                mProgramList.apply(Objects.requireNonNull(chunk, "Chunk cannot be null"));
             }
         });
     }