Added Application Suggestions.

Added in custom Resolver to handle providing suggestions.

Added in Service to handle providing suggestions to custom resolver.

Added in ability to provider suggestions through a Proxy to another
application which must be installed during compile time if one is
to be used. This is a similar implementation to how the Location
Services work.

Change-Id: Id960260596b7bb6485caa1e1d07744e387a4c6e9
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestManagerService.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestManagerService.java
new file mode 100644
index 0000000..d7a6ad4
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestManagerService.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.cyanogenmod.platform.internal;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.SystemService;
+
+import cyanogenmod.app.CMContextConstants;
+import cyanogenmod.app.suggest.ApplicationSuggestion;
+import cyanogenmod.app.suggest.IAppSuggestManager;
+import cyanogenmod.platform.Manifest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppSuggestManagerService extends SystemService {
+    private static final String TAG = "AppSgstMgrService";
+    public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    public static final String NAME = "appsuggest";
+
+    public static final String ACTION = "org.cyanogenmod.app.suggest";
+
+    private AppSuggestProviderInterface mImpl;
+
+    private final IBinder mService = new IAppSuggestManager.Stub() {
+        public boolean handles(Intent intent) {
+            if (mImpl == null) return false;
+
+            return mImpl.handles(intent);
+        }
+
+        public List<ApplicationSuggestion> getSuggestions(Intent intent) {
+            if (mImpl == null) return new ArrayList<>(0);
+
+            return mImpl.getSuggestions(intent);
+        }
+    };
+
+    public AppSuggestManagerService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        mImpl = AppSuggestProviderProxy.createAndBind(mContext, TAG, ACTION,
+                R.bool.config_enableAppSuggestOverlay,
+                R.string.config_appSuggestProviderPackageName,
+                R.array.config_appSuggestProviderPackageNames);
+        if (mImpl == null) {
+            Slog.e(TAG, "no app suggest provider found");
+        } else {
+            Slog.i(TAG, "Bound to to suggest provider");
+        }
+
+        publishBinderService(CMContextConstants.CM_APP_SUGGEST_SERVICE, mService);
+    }
+}
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderInterface.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderInterface.java
new file mode 100644
index 0000000..da815ce
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderInterface.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.cyanogenmod.platform.internal;
+
+import android.content.Intent;
+import cyanogenmod.app.suggest.ApplicationSuggestion;
+
+import java.util.List;
+
+/**
+ * App Suggestion Manager's interface for Applicaiton Suggestion Providers.
+ *
+ * @hide
+ */
+public interface AppSuggestProviderInterface {
+    boolean handles(Intent intent);
+    List<ApplicationSuggestion> getSuggestions(Intent intent);
+}
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderProxy.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderProxy.java
new file mode 100644
index 0000000..0357f73
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderProxy.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.cyanogenmod.platform.internal;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import com.android.server.ServiceWatcher;
+
+import cyanogenmod.app.suggest.ApplicationSuggestion;
+import cyanogenmod.app.suggest.IAppSuggestProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class AppSuggestProviderProxy implements AppSuggestProviderInterface {
+    private static final String TAG = AppSuggestProviderProxy.class.getSimpleName();
+    private static final boolean DEBUG = AppSuggestManagerService.DEBUG;
+
+    public static AppSuggestProviderProxy createAndBind(
+            Context context, String name, String action,
+            int overlaySwitchResId, int defaultServicePackageNameResId,
+            int initialPackageNamesResId) {
+        AppSuggestProviderProxy proxy = new AppSuggestProviderProxy(context, name, action,
+                overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId);
+        if (proxy.bind()) {
+            return proxy;
+        } else {
+            return null;
+        }
+    }
+
+    private Context mContext;
+    private ServiceWatcher mServiceWatcher;
+
+    private AppSuggestProviderProxy(Context context, String name, String action,
+            int overlaySwitchResId, int defaultServicePackageNameResId,
+            int initialPackageNamesResId) {
+        mContext = context;
+        mServiceWatcher = new ServiceWatcher(mContext, TAG + "-" + name, action, overlaySwitchResId,
+                defaultServicePackageNameResId, initialPackageNamesResId, null, null);
+    }
+
+    private boolean bind() {
+        return mServiceWatcher.start();
+    }
+
+    private IAppSuggestProvider getService() {
+        return IAppSuggestProvider.Stub.asInterface(mServiceWatcher.getBinder());
+    }
+
+    @Override
+    public boolean handles(Intent intent) {
+        IAppSuggestProvider service = getService();
+        if (service == null) return false;
+
+        try {
+            return service.handles(intent);
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+        } catch (Exception e) {
+            // never let remote service crash system server
+            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+        }
+        return false;
+    }
+
+    @Override
+    public List<ApplicationSuggestion> getSuggestions(Intent intent) {
+        IAppSuggestProvider service = getService();
+        if (service == null) return new ArrayList<>(0);
+
+        try {
+            return service.getSuggestions(intent);
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+        } catch (Exception e) {
+            // never let remote service crash system server
+            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+        }
+        return new ArrayList<>(0);
+    }
+}
diff --git a/cm/res/AndroidManifest.xml b/cm/res/AndroidManifest.xml
index 8c40827..ec801cd 100644
--- a/cm/res/AndroidManifest.xml
+++ b/cm/res/AndroidManifest.xml
@@ -120,6 +120,13 @@
                 android:description="@string/permdesc_managePersistentStorage"
                 android:protectionLevel="system|signature" />
 
+    <!-- Permission for accessing a provider of app suggestions
+         @hide -->
+    <permission android:name="cyanogenmod.permission.ACCESS_APP_SUGGESTIONS"
+                android:label="@string/permlab_accessAppSuggestions"
+                android:description="@string/permdesc_accessAppSuggestions"
+                android:protectionLevel="signature|system|development" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/cm/res/res/values/config.xml b/cm/res/res/values/config.xml
new file mode 100644
index 0000000..eb8982d
--- /dev/null
+++ b/cm/res/res/values/config.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The CyanogenMod Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- Whether to enable app suggest overlay which allows app suggest
+         provider to be replaced by an app at run-time. When disabled, only
+         the config_appSuggestProviderPackageName will be searched for app
+         suggest provider, otherwise packages whos signature matches the
+         signature of config_appSuggestProviderPackageNames will be searched,
+         and the service with the highest version number will be picked.
+         Anyone who wants to disable the overlay mechanism can set it to false.
+
+         Note: There appears to be an issue with false if we reinstall the provider which causes
+         it to not get the update and fail to reconnect on package update.  It's safer to just
+         use the list version with config_appSuggestProviderPackageNames.
+         -->
+    <bool name="config_enableAppSuggestOverlay" translatable="false">true</bool>
+
+    <!-- Package name providing app suggest support. Used only when
+         config_enableAppSuggestOverlay is false. -->
+    <string name="config_appSuggestProviderPackageName" translatable="false">com.cyanogen.app.suggest</string>
+
+    <!-- List of packages providing app suggest support. Used only when
+    config_enableAppSuggestOverlay is true. -->
+    <string-array name="config_appSuggestProviderPackageNames" translatable="false">
+        <item>com.cyanogen.app.suggest</item>
+    </string-array>
+</resources>
\ No newline at end of file
diff --git a/cm/res/res/values/strings.xml b/cm/res/res/values/strings.xml
index e727080..bcfed5a 100644
--- a/cm/res/res/values/strings.xml
+++ b/cm/res/res/values/strings.xml
@@ -70,6 +70,10 @@
     <string name="permlab_managePersistentStorage">manage persistent storage</string>
     <string name="permdesc_managePersistentStorage">Allows an app to read or write properties which may persist thrοugh a factory reset.</string>
 
+    <!-- Labels for the ACCESS_APP_SUGGESTIONS permission -->
+    <string name="permlab_accessAppSuggestions">access application suggestions</string>
+    <string name="permdesc_accessAppSuggestions">Allows an app to access application suggestions.</string>
+
     <!-- Label to show for a service that is running because it is observing the user's custom tiles. -->
     <string name="custom_tile_listener_binding_label">Custom tile listener</string>
 
diff --git a/cm/res/res/values/symbols.xml b/cm/res/res/values/symbols.xml
index 3dcf497..7977939 100644
--- a/cm/res/res/values/symbols.xml
+++ b/cm/res/res/values/symbols.xml
@@ -19,6 +19,10 @@
          SDK.  Instead, put them here. -->
     <private-symbols package="org.cyanogenmod.platform.internal" />
 
+    <java-symbol type="bool" name="config_enableAppSuggestOverlay"/>
+    <java-symbol type="string" name="config_appSuggestProviderPackageName"/>
+    <java-symbol type="array" name="config_appSuggestProviderPackageNames"/>
+
     <java-symbol type="string" name="custom_tile_listener_binding_label" />
 
     <!-- Profiles -->