Add more checks for manifest

Change-Id: I1bdfa35fe6ae6d026a913abc95b209f0fe41e2fb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99500
Commit-Queue: Keerti Parthasarathy <keertip@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/manifest/manifest_validator.dart b/pkg/analyzer/lib/src/manifest/manifest_validator.dart
index f18f9bd..c83e616 100644
--- a/pkg/analyzer/lib/src/manifest/manifest_validator.dart
+++ b/pkg/analyzer/lib/src/manifest/manifest_validator.dart
@@ -40,9 +40,11 @@
       var features = manifest?.getElementsByTagName(USES_FEATURE_TAG) ?? [];
       var permissions =
           manifest?.getElementsByTagName(USES_PERMISSION_TAG) ?? [];
+      var activities = _findActivityElements(manifest);
 
       _validateFeatures(features, reporter);
       _validatePermissions(permissions, features, reporter);
+      _validateActivities(activities, reporter);
     }
     return recorder.errors;
   }
@@ -93,6 +95,39 @@
     });
   }
 
+  /*
+   * Validate the 'activity' tags.
+   */
+  _validateActivities(List<Element> activites, ErrorReporter reporter) {
+    activites.forEach((activity) {
+      var attributes = activity.attributes;
+      if (attributes.containsKey(ATTRIBUTE_SCREEN_ORIENTATION)) {
+        var value = attributes[ATTRIBUTE_SCREEN_ORIENTATION];
+        if (UNSUPPORTED_ORIENTATIONS
+            .contains(attributes[ATTRIBUTE_SCREEN_ORIENTATION])) {
+          _reportErrorForNode(reporter, activity, ATTRIBUTE_SCREEN_ORIENTATION,
+              ManifestWarningCode.SETTING_ORIENTATION_ON_ACTIVITY);
+        }
+      }
+      if (attributes.containsKey(ATTRIBUTE_RESIZEABLE_ACTIVITY)) {
+        if (attributes[ATTRIBUTE_RESIZEABLE_ACTIVITY] == 'false') {
+          _reportErrorForNode(reporter, activity, ATTRIBUTE_RESIZEABLE_ACTIVITY,
+              ManifestWarningCode.NON_RESIZABLE_ACTIVITY);
+        }
+      }
+    });
+  }
+
+  List<Element> _findActivityElements(Element manifest) {
+    var applications = manifest?.getElementsByTagName(APPLICATION_TAG);
+    var applicationElement = (applications != null && applications.isNotEmpty)
+        ? applications.first
+        : null;
+    var activities =
+        applicationElement?.getElementsByTagName(ACTIVITY_TAG) ?? [];
+    return activities;
+  }
+
   bool _hasFeatureCamera(List<Element> features) =>
       features.any((f) => f.localName == HARDWARE_FEATURE_CAMERA);
 
diff --git a/pkg/analyzer/lib/src/manifest/manifest_values.dart b/pkg/analyzer/lib/src/manifest/manifest_values.dart
index ea1b59d..db96a93 100644
--- a/pkg/analyzer/lib/src/manifest/manifest_values.dart
+++ b/pkg/analyzer/lib/src/manifest/manifest_values.dart
@@ -5,7 +5,7 @@
 /*
 *  The arritbute values to check for compatibiltiy with Chrome OS.
 *
- */
+*/
 
 const String MANIFEST_TAG = 'manifest';
 
@@ -13,10 +13,21 @@
 
 const String USES_FEATURE_TAG = 'uses-feature';
 
+const String APPLICATION_TAG = 'application';
+
+const String ACTIVITY_TAG = 'activity';
+
 const String ANDROID_NAME = 'android:name';
 
 const String ANDROID_REQUIRED = 'android:required';
 
+// The parser does not maintain camelcase for attributes
+// Use 'resizeableactivity' instead of 'resizeableActivity'
+const String ATTRIBUTE_RESIZEABLE_ACTIVITY = 'android:resizeableactivity';
+
+// Use 'screenorientation' instead of 'screenOrientation'
+const String ATTRIBUTE_SCREEN_ORIENTATION = 'android:screenorientation';
+
 const String HARDWARE_FEATURE_CAMERA = 'android.hardware.camera';
 
 const String HARDWARE_FEATURE_CAMERA_AUTOFOCUS =
@@ -99,3 +110,14 @@
       return null;
   }
 }
+
+const UNSUPPORTED_ORIENTATIONS = <String>[
+  'landscape',
+  'portrait',
+  'reverseLandscape',
+  'reversePortrait',
+  'sensorLandscape',
+  'sensorPortrait',
+  'userLandscape',
+  'userPortrait'
+];
diff --git a/pkg/analyzer/lib/src/manifest/manifest_warning_code.dart b/pkg/analyzer/lib/src/manifest/manifest_warning_code.dart
index 0e0928c..8cd566c 100644
--- a/pkg/analyzer/lib/src/manifest/manifest_warning_code.dart
+++ b/pkg/analyzer/lib/src/manifest/manifest_warning_code.dart
@@ -46,6 +46,29 @@
               "Try adding the uses-feature with required=\"false\" attribute value.");
 
   /**
+   * A code indicating that the activity is set to be non resizable.
+   */
+  static const ManifestWarningCode NON_RESIZABLE_ACTIVITY =
+      const ManifestWarningCode(
+          'NON_RESIZABLE_ACTIVITY',
+          "The `<activity>` element should be allowed to be resized to allow " +
+              "users to take advantage of the multi-window environment on Chrome OS",
+          correction: "Consider declaring the corresponding " +
+              "activity element with `resizableActivity=\"true\"` attribute.");
+
+  /**
+   * A code indicating that the activity is locked to an orientation.
+   */
+  static const ManifestWarningCode SETTING_ORIENTATION_ON_ACTIVITY =
+      const ManifestWarningCode(
+          'SETTING_ORIENTATION_ON_ACTIVITY',
+          "The `<activity>` element should not be locked to any orientation so " +
+              "that users can take advantage of the multi-window environments " +
+              "and larger screens on Chrome OS",
+          correction: "Consider declaring the corresponding activity element with" +
+              " `screenOrientation=\"unspecified\"` or `\"fullSensor\"` attribute.");
+
+  /**
    * Initialize a newly created warning code to have the given [name], [message]
    * and [correction].
    */
diff --git a/pkg/analyzer/test/src/manifest/manifest_validator_test.dart b/pkg/analyzer/test/src/manifest/manifest_validator_test.dart
index 8cd846cc6..1388f40 100644
--- a/pkg/analyzer/test/src/manifest/manifest_validator_test.dart
+++ b/pkg/analyzer/test/src/manifest/manifest_validator_test.dart
@@ -65,10 +65,42 @@
 ''', [ManifestWarningCode.CAMERA_PERMISSIONS_INCOMPATIBLE]);
   }
 
+  test_screenOrientation_error() {
+    assertErrors('''
+<manifest
+     xmlns:android="http://schemas.android.com/apk/res/android">
+  <application android:label="@string/app_name">
+    <activity android:name="testActivity"
+      android:screenOrientation="landscape"
+      android:exported="false">
+    </activity>
+  </application>
+</manifest>
+''', [ManifestWarningCode.SETTING_ORIENTATION_ON_ACTIVITY]);
+  }
+
+  test_resizeableactivity_error() {
+    assertErrors('''
+<manifest
+     xmlns:android="http://schemas.android.com/apk/res/android">
+   <application android:label="@string/app_name">
+  <activity android:name="testActivity"
+    android:resizeableActivity="false"
+    android:exported="false">
+  </activity>
+  </application>
+</manifest>
+''', [ManifestWarningCode.NON_RESIZABLE_ACTIVITY]);
+  }
+
   test_no_errors() {
     assertErrors('''
 <manifest
      xmlns:android="http://schemas.android.com/apk/res/android">
+  <activity android:name="testActivity"
+    android:resizeableActivity="true"
+    android:exported="false">
+  </activity>
 </manifest>
 ''', []);
   }
diff --git a/pkg/analyzer/test/src/task/options_test.dart b/pkg/analyzer/test/src/task/options_test.dart
index 69942af..11182d9 100644
--- a/pkg/analyzer/test/src/task/options_test.dart
+++ b/pkg/analyzer/test/src/task/options_test.dart
@@ -18,6 +18,7 @@
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/lint/linter.dart';
 import 'package:analyzer/src/lint/registry.dart';
+import 'package:analyzer/src/manifest/manifest_warning_code.dart';
 import 'package:analyzer/src/task/options.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:test/test.dart';
@@ -237,6 +238,9 @@
         declaredNames.remove('TODO_REGEX');
       } else if (errorType == ParserErrorCode) {
         declaredNames.remove('CONST_AFTER_FACTORY');
+      } else if (errorType == ManifestWarningCode) {
+        declaredNames.remove('NON_RESIZABLE_ACTIVITY');
+        declaredNames.remove('SETTING_ORIENTATION_ON_ACTIVITY');
       }
 
       // Assert that all remaining declared names are in errorCodeValues