Launch airplane mode setting only when setting is available

Prompt airplane mode setting only when airplane mode setting is
available in device.

Also added code to handle ActivityNotFoundException gracefully.

Bug: 23778285

Change-Id: Ia3fa3633151075219eb484847c42bd77b981a25d
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
index c18ccd6..ad2ead6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
@@ -17,18 +17,16 @@
 
 package com.android.cts.verifier.sensors.base;
 
+import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.TestResult;
 import com.android.cts.verifier.sensors.helpers.SensorFeaturesDeactivator;
 import com.android.cts.verifier.sensors.reporting.SensorTestDetails;
 
-import junit.framework.Assert;
-
-//import android.app.Activity;
-import com.android.cts.verifier.PassFailButtons;
-
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.hardware.cts.helpers.ActivityResultMultiplexedLatch;
 import android.media.MediaPlayer;
 import android.opengl.GLSurfaceView;
@@ -45,6 +43,7 @@
 import android.widget.ScrollView;
 import android.widget.TextView;
 
+import junit.framework.Assert;
 import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
@@ -326,11 +325,36 @@
     @Override
     public int executeActivity(Intent intent) throws InterruptedException {
         ActivityResultMultiplexedLatch.Latch latch = mActivityResultMultiplexedLatch.bindThread();
-        startActivityForResult(intent, latch.getRequestCode());
+        try {
+            startActivityForResult(intent, latch.getRequestCode());
+        } catch (ActivityNotFoundException e) {
+            // handle exception gracefully
+            // Among all defined activity results, RESULT_CANCELED offers the semantic closest to
+            // represent absent setting activity.
+            return RESULT_CANCELED;
+        }
         return latch.await();
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean hasSystemFeature(String feature) {
+        PackageManager pm = getPackageManager();
+        return pm.hasSystemFeature(feature);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean hasActivity(String action) {
+        PackageManager pm = getPackageManager();
+        return pm.resolveActivity(new Intent(action), PackageManager.MATCH_DEFAULT_ONLY) != null;
+    }
+
+    /**
      * Initializes and shows the {@link GLSurfaceView} available to tests.
      * NOTE: initialization can be performed only once, usually inside {@link #activitySetUp()}.
      */
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/ISensorTestStateContainer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/ISensorTestStateContainer.java
index 2ba74e3..1df34ae 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/ISensorTestStateContainer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/ISensorTestStateContainer.java
@@ -70,4 +70,16 @@
      * @return The {@link ContentResolver} associated with the test.
      */
     ContentResolver getContentResolver();
+
+    /**
+     * @param feature the feature being tested
+     * @return true if the specified feature is implemented; false otherwise.
+     */
+    boolean hasSystemFeature(String feature);
+
+    /**
+     * @param action setting in the form of action name
+     * @return true if corresponding setting activity exists; false otherwise.
+     */
+    boolean hasActivity(String action);
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorFeaturesDeactivator.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorFeaturesDeactivator.java
index 36559bd..596444f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorFeaturesDeactivator.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorFeaturesDeactivator.java
@@ -20,6 +20,7 @@
 import com.android.cts.verifier.sensors.base.ISensorTestStateContainer;
 
 import android.content.ContentResolver;
+import android.content.pm.PackageManager;
 import android.os.Build;
 import android.provider.Settings;
 
@@ -91,7 +92,7 @@
 
     private class AirplaneModeSettingContainer extends SensorSettingContainer {
         public AirplaneModeSettingContainer() {
-            super(Settings.ACTION_WIRELESS_SETTINGS, R.string.snsr_setting_airplane_mode);
+            super(Settings.ACTION_AIRPLANE_MODE_SETTINGS, R.string.snsr_setting_airplane_mode);
         }
 
         @Override
@@ -106,6 +107,13 @@
                         .getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, defaultValue);
             }
         }
+
+        @Override
+        protected boolean isSettingAvailable() {
+            // call base first, lean back UI device does not have airplane mode
+            return super.isSettingAvailable() &&
+                    !(mStateContainer.hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+        }
     }
 
     private class ScreenBrightnessModeSettingContainer extends SensorSettingContainer {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorSettingContainer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorSettingContainer.java
index 2d44d8d..25b4a7f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorSettingContainer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorSettingContainer.java
@@ -55,7 +55,7 @@
     public synchronized void requestToSetMode(
             ISensorTestStateContainer stateContainer,
             boolean modeOn) throws InterruptedException {
-        if (!isSettingAvailable()) {
+        if (!isSettingAvailable() && !isSettingUiAvailable(stateContainer)) {
             return;
         }
         trySetMode(stateContainer, modeOn);
@@ -70,7 +70,7 @@
 
     public synchronized void requestToResetMode(ISensorTestStateContainer stateContainer)
             throws InterruptedException {
-        if (!isSettingAvailable()) {
+        if (!isSettingAvailable() || !isSettingUiAvailable(stateContainer)) {
             return;
         }
         trySetMode(stateContainer, mCapturedModeOn);
@@ -99,7 +99,7 @@
         return stateContainer.getString(mSettingNameResId);
     }
 
-    private boolean isSettingAvailable() {
+    protected boolean isSettingAvailable() {
         if (!mInitialized) {
             throw new IllegalStateException(
                     "Object must be initialized first by invoking #captureInitialState.");
@@ -107,5 +107,9 @@
         return mSettingAvailable;
     }
 
+    protected boolean isSettingUiAvailable(ISensorTestStateContainer stateContainer) {
+        return stateContainer.hasActivity(mAction);
+    }
+
     protected abstract int getSettingMode(int defaultValue);
 }