1be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov/*
2be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov** Copyright 2014, The Android Open Source Project
3be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov**
4be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov** Licensed under the Apache License, Version 2.0 (the "License");
5be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov** you may not use this file except in compliance with the License.
6be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov** You may obtain a copy of the License at
7be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov**
8be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov**     http://www.apache.org/licenses/LICENSE-2.0
9be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov**
10be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov** Unless required by applicable law or agreed to in writing, software
11be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov** distributed under the License is distributed on an "AS IS" BASIS,
12be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov** See the License for the specific language governing permissions and
14be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov** limitations under the License.
15be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov*/
16be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
17be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganovpackage com.android.commands.appwidget;
18be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
19be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganovimport android.content.Context;
20be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganovimport android.os.IBinder;
21be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganovimport android.os.RemoteException;
22be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganovimport android.os.ServiceManager;
23be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganovimport android.os.UserHandle;
24be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganovimport android.text.TextUtils;
25be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
26be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganovimport com.android.internal.appwidget.IAppWidgetService;
27be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
28be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov/**
29be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov * This class is a command line utility for manipulating app widgets. A client
30be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov * can grant or revoke the permission for a given package to bind app widgets.
31be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov */
32be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganovpublic class AppWidget {
33be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
34be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov    private static final String USAGE =
35be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        "usage: adb shell appwidget [subcommand] [options]\n"
36be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "\n"
37be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "usage: adb shell appwidget grantbind --package <PACKAGE> "
38be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                + " [--user <USER_ID> | current]\n"
39be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  <PACKAGE> an Android package name.\n"
40be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  <USER_ID> The user id under which the package is installed.\n"
41be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  Example:\n"
42be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  # Grant the \"foo.bar.baz\" package to bind app widgets for the current user.\n"
43be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  adb shell grantbind --package foo.bar.baz --user current\n"
44be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "\n"
45be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "usage: adb shell appwidget revokebind --package <PACKAGE> "
46be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                + "[--user <USER_ID> | current]\n"
47be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  <PACKAGE> an Android package name.\n"
48be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  <USER_ID> The user id under which the package is installed.\n"
49be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  Example:\n"
50be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  # Revoke the permisison to bind app widgets from the \"foo.bar.baz\" package.\n"
51be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "  adb shell revokebind --package foo.bar.baz --user current\n"
52be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        + "\n";
53be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
54be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov    private static class Parser {
55be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private static final String ARGUMENT_GRANT_BIND = "grantbind";
56be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private static final String ARGUMENT_REVOKE_BIND = "revokebind";
57be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private static final String ARGUMENT_PACKAGE = "--package";
58be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private static final String ARGUMENT_USER = "--user";
59be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private static final String ARGUMENT_PREFIX = "--";
60be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private static final String VALUE_USER_CURRENT = "current";
61be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
62be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private final Tokenizer mTokenizer;
63be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
64be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        public Parser(String[] args) {
65be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            mTokenizer = new Tokenizer(args);
66be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        }
67be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
68be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        public Runnable parseCommand() {
69be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            try {
70be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                String operation = mTokenizer.nextArg();
71be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                if (ARGUMENT_GRANT_BIND.equals(operation)) {
72be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                    return parseSetGrantBindAppWidgetPermissionCommand(true);
73be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                } else if (ARGUMENT_REVOKE_BIND.equals(operation)) {
74be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                    return parseSetGrantBindAppWidgetPermissionCommand(false);
75be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                } else {
76be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                    throw new IllegalArgumentException("Unsupported operation: " + operation);
77be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                }
78be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            } catch (IllegalArgumentException iae) {
79be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                System.out.println(USAGE);
80be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                System.out.println("[ERROR] " + iae.getMessage());
81be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                return null;
82be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            }
83be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        }
84be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
85be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private SetBindAppWidgetPermissionCommand parseSetGrantBindAppWidgetPermissionCommand(
86be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                boolean granted) {
87be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            String packageName = null;
88b52c7330d986e62812fd7c1b77020629e8ff7930Xiaohui Chen            int userId = UserHandle.USER_SYSTEM;
89be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            for (String argument; (argument = mTokenizer.nextArg()) != null;) {
90be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                if (ARGUMENT_PACKAGE.equals(argument)) {
91be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                    packageName = argumentValueRequired(argument);
92be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                } else if (ARGUMENT_USER.equals(argument)) {
93be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                    String user = argumentValueRequired(argument);
94be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                    if (VALUE_USER_CURRENT.equals(user)) {
95be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                        userId = UserHandle.USER_CURRENT;
96be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                    } else {
97be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                        userId = Integer.parseInt(user);
98be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                    }
99be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                } else {
100be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                    throw new IllegalArgumentException("Unsupported argument: " + argument);
101be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                }
102be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            }
103be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            if (packageName == null) {
104be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                throw new IllegalArgumentException("Package name not specified."
105be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                        + " Did you specify --package argument?");
106be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            }
107be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            return new SetBindAppWidgetPermissionCommand(packageName, granted, userId);
108be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        }
109be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
110be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private String argumentValueRequired(String argument) {
111be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            String value = mTokenizer.nextArg();
112be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) {
113be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                throw new IllegalArgumentException("No value for argument: " + argument);
114be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            }
115be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            return value;
116be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        }
117be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov    }
118be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
119be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov    private static class Tokenizer {
120be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private final String[] mArgs;
121be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private int mNextArg;
122be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
123be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        public Tokenizer(String[] args) {
124be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            mArgs = args;
125be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        }
126be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
127be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        private String nextArg() {
128be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            if (mNextArg < mArgs.length) {
129be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                return mArgs[mNextArg++];
130be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            } else {
131be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                return null;
132be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            }
133be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        }
134be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov    }
135be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
136be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov    private static class SetBindAppWidgetPermissionCommand implements Runnable {
137be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        final String mPackageName;
138be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        final boolean mGranted;
139be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        final int mUserId;
140be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
141be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        public SetBindAppWidgetPermissionCommand(String packageName, boolean granted,
142be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                int userId) {
143be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            mPackageName = packageName;
144be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            mGranted = granted;
145be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            mUserId = userId;
146be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        }
147be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
148be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        @Override
149be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        public void run() {
150be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            IBinder binder = ServiceManager.getService(Context.APPWIDGET_SERVICE);
151be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            IAppWidgetService appWidgetService = IAppWidgetService.Stub.asInterface(binder);
152be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            try {
15339999cf0263d0568337a87d0e586dcafd9e22ba1Svetoslav                appWidgetService.setBindAppWidgetPermission(mPackageName, mUserId, mGranted);
154be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            } catch (RemoteException re) {
155be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov                re.printStackTrace();
156be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            }
157be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        }
158be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov    }
159be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov
160be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov    public static void main(String[] args) {
161be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        Parser parser = new Parser(args);
162be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        Runnable command = parser.parseCommand();
163be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        if (command != null) {
164be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov            command.run();
165be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov        }
166be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov    }
167be19c3a91a11508bd12e3355f3b1d9adf7f53194Svet Ganov}
168