Implement feature #2117336: Create event communication APIs for live wallpaper

Note: currently only implements an async version (no result), and not yet
actually tested.

Change-Id: Id47ed045a4b0eb309ea8c58daf41a0e03eff1d3a
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 69c87ee..5881694 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -592,6 +592,31 @@
     }
     
     /**
+     * Send an arbitrary command to the current active wallpaper.
+     * 
+     * @param windowToken The window who these offsets should be associated
+     * with, as returned by {@link android.view.View#getWindowToken()
+     * View.getWindowToken()}.
+     * @param action Name of the command to perform.  This must be a scoped
+     * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT".
+     * @param x Arbitrary integer argument based on command.
+     * @param y Arbitrary integer argument based on command.
+     * @param z Arbitrary integer argument based on command.
+     * @param extras Optional additional information for the command, or null.
+     */
+    public void sendWallpaperCommand(IBinder windowToken, String action,
+            int x, int y, int z, Bundle extras) {
+        try {
+            //Log.v(TAG, "Sending new wallpaper offsets from app...");
+            ViewRoot.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
+                    windowToken, action, x, y, z, extras, false);
+            //Log.v(TAG, "...app returning after sending offsets!");
+        } catch (RemoteException e) {
+            // Ignore.
+        }
+    }
+    
+    /**
      * Clear the offsets previously associated with this window through
      * {@link #setWallpaperOffsets(IBinder, float, float)}.  This reverts
      * the window to its default state, where it does not cause the wallpaper
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index da8d62c0..dfd6af9 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -27,6 +27,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -73,11 +74,21 @@
     private static final int MSG_UPDATE_SURFACE = 10000;
     private static final int MSG_VISIBILITY_CHANGED = 10010;
     private static final int MSG_WALLPAPER_OFFSETS = 10020;
+    private static final int MSG_WALLPAPER_COMMAND = 10025;
     private static final int MSG_WINDOW_RESIZED = 10030;
     private static final int MSG_TOUCH_EVENT = 10040;
     
     private Looper mCallbackLooper;
     
+    static final class WallpaperCommand {
+        String action;
+        int x;
+        int y;
+        int z;
+        Bundle extras;
+        boolean sync;
+    }
+    
     /**
      * The actual implementation of a wallpaper.  A wallpaper service may
      * have multiple instances running (for example as a real wallpaper
@@ -233,6 +244,22 @@
                 }
             }
             
+            public void dispatchWallpaperCommand(String action, int x, int y,
+                    int z, Bundle extras, boolean sync) {
+                synchronized (mLock) {
+                    if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y);
+                    WallpaperCommand cmd = new WallpaperCommand();
+                    cmd.action = action;
+                    cmd.x = x;
+                    cmd.y = y;
+                    cmd.z = z;
+                    cmd.extras = extras;
+                    cmd.sync = sync;
+                    Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND);
+                    msg.obj = cmd;
+                    mCaller.sendMessage(msg);
+                }
+            }
         };
         
         /**
@@ -338,6 +365,28 @@
         }
         
         /**
+         * Process a command that was sent to the wallpaper with
+         * {@link WallpaperManager#sendWallpaperCommand(String, int, int, int, Bundle)}.
+         * The default implementation does nothing, and always returns null
+         * as the result.
+         * 
+         * @param action The name of the command to perform.  This tells you
+         * what to do and how to interpret the rest of the arguments.
+         * @param x Generic integer parameter.
+         * @param y Generic integer parameter.
+         * @param z Generic integer parameter.
+         * @param extras Any additional parameters.
+         * @param resultRequested If true, the caller is requesting that
+         * a result, appropriate for the command, be returned back.
+         * @return If returning a result, create a Bundle and place the
+         * result data in to it.  Otherwise return null.
+         */
+        public Bundle onCommand(String action, int x, int y, int z,
+                Bundle extras, boolean resultRequested) {
+            return null;
+        }
+        
+        /**
          * Called when an application has changed the desired virtual size of
          * the wallpaper.
          */
@@ -585,6 +634,23 @@
             }
         }
         
+        void doCommand(WallpaperCommand cmd) {
+            Bundle result;
+            if (!mDestroyed) {
+                result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
+                        cmd.extras, cmd.sync);
+            } else {
+                result = null;
+            }
+            if (cmd.sync) {
+                try {
+                    if (DEBUG) Log.v(TAG, "Reporting command complete");
+                    mSession.wallpaperCommandComplete(mWindow.asBinder(), result);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+        
         void detach() {
             mDestroyed = true;
             
@@ -709,6 +775,10 @@
                 case MSG_WALLPAPER_OFFSETS: {
                     mEngine.doOffsetsChanged();
                 } break;
+                case MSG_WALLPAPER_COMMAND: {
+                    WallpaperCommand cmd = (WallpaperCommand)message.obj;
+                    mEngine.doCommand(cmd);
+                } break;
                 case MSG_WINDOW_RESIZED: {
                     final boolean reportDraw = message.arg1 != 0;
                     mEngine.updateSurface(true, false);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 7977578..6bfc8b5 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -18,11 +18,11 @@
 package android.view;
 
 import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
-import android.os.ParcelFileDescriptor;
-
 /**
  * API back to a client window that the Window Manager uses to inform it of
  * interesting things happening.
@@ -63,4 +63,7 @@
      * Called for wallpaper windows when their offsets change.
      */
     void dispatchWallpaperOffsets(float x, float y, boolean sync);
+    
+    void dispatchWallpaperCommand(String action, int x, int y,
+            int z, in Bundle extras, boolean sync);
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 9b8b6d4..7e7a38f 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -19,6 +19,7 @@
 
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Bundle;
 import android.view.IWindow;
 import android.view.MotionEvent;
 import android.view.WindowManager;
@@ -116,4 +117,9 @@
     void setWallpaperPosition(IBinder windowToken, float x, float y);
     
     void wallpaperOffsetsComplete(IBinder window);
+    
+    Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
+            int z, in Bundle extras, boolean sync);
+    
+    void wallpaperCommandComplete(IBinder window, in Bundle result);
 }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 3b83044..f4593f5 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2903,6 +2903,16 @@
                 }
             }
         }
+        
+        public void dispatchWallpaperCommand(String action, int x, int y,
+                int z, Bundle extras, boolean sync) {
+            if (sync) {
+                try {
+                    sWindowSession.wallpaperCommandComplete(asBinder(), null);
+                } catch (RemoteException e) {
+                }
+            }
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 38ef0c2..2674262 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -1,6 +1,7 @@
 package com.android.internal.view;
 
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.view.IWindow;
@@ -101,4 +102,14 @@
             }
         }
     }
+    
+    public void dispatchWallpaperCommand(String action, int x, int y,
+            int z, Bundle extras, boolean sync) {
+        if (sync) {
+            try {
+                mSession.wallpaperCommandComplete(asBinder(), null);
+            } catch (RemoteException e) {
+            }
+        }
+    }
 }