1b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera/*
2b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera * Copyright (C) 2014 The Android Open Source Project
3b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera *
4b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera * Licensed under the Apache License, Version 2.0 (the "License");
5b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera * you may not use this file except in compliance with the License.
6b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera * You may obtain a copy of the License at
7b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera *
8b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera *      http://www.apache.org/licenses/LICENSE-2.0
9b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera *
10b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera * Unless required by applicable law or agreed to in writing, software
11b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera * distributed under the License is distributed on an "AS IS" BASIS,
12b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera * See the License for the specific language governing permissions and
14b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera * limitations under the License.
15b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera */
16b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
17b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talaverapackage com.android.commands.dpm;
18b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
19dc589ac82b5fe2063f4cfd94c8ae26d43d5420a0Sudheer Shankaimport android.app.ActivityManager;
206bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolovimport android.app.IActivityManager;
21afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadwayimport android.app.admin.DevicePolicyManager;
22b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talaveraimport android.app.admin.IDevicePolicyManager;
239c17388cff98fba9679ce6457ac5003a660c6013Esteban Talaveraimport android.content.ComponentName;
24b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talaveraimport android.content.Context;
25b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talaveraimport android.os.RemoteException;
26b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talaveraimport android.os.ServiceManager;
279c17388cff98fba9679ce6457ac5003a660c6013Esteban Talaveraimport android.os.UserHandle;
28b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
29b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talaveraimport com.android.internal.os.BaseCommand;
30b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
31b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talaveraimport java.io.PrintStream;
32b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
33b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talaverapublic final class Dpm extends BaseCommand {
34b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
35b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    /**
36b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera     * Command-line entry point.
37b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera     *
38b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera     * @param args The command-line arguments
39b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera     */
40b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    public static void main(String[] args) {
41b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera      (new Dpm()).run(args);
42b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    }
43b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
44e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban    private static final String COMMAND_SET_ACTIVE_ADMIN = "set-active-admin";
45b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    private static final String COMMAND_SET_DEVICE_OWNER = "set-device-owner";
4641de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera    private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
4738dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy    private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
48b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
49b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    private IDevicePolicyManager mDevicePolicyManager;
50b52c7330d986e62812fd7c1b77020629e8ff7930Xiaohui Chen    private int mUserId = UserHandle.USER_SYSTEM;
51a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki    private String mName = "";
52e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban    private ComponentName mComponent = null;
53b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
54b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    @Override
55b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    public void onShowUsage(PrintStream out) {
569c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera        out.println(
579c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera                "usage: dpm [subcommand] [options]\n" +
586bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                "usage: dpm set-active-admin [ --user <USER_ID> | current ] <COMPONENT>\n" +
5958b684f1cdc52467b71c42cfae18433a19ce7d0bMakoto Onuki                // STOPSHIP Finalize it
606bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                "usage: dpm set-device-owner [ --user <USER_ID> | current *EXPERIMENTAL* ] " +
616bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                "[ --name <NAME> ] <COMPONENT>\n" +
626bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                "usage: dpm set-profile-owner [ --user <USER_ID> | current ] [ --name <NAME> ] " +
636bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                "<COMPONENT>\n" +
6438dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy                "usage: dpm remove-active-admin [ --user <USER_ID> | current ] [ --name <NAME> ] " +
6538dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy                "<COMPONENT>\n" +
66b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera                "\n" +
67e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban                "dpm set-active-admin: Sets the given component as active admin" +
68e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban                " for an existing user.\n" +
69e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban                "\n" +
706bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                "dpm set-device-owner: Sets the given component as active admin, and its" +
716bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                " package as device owner.\n" +
7241de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera                "\n" +
7341de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera                "dpm set-profile-owner: Sets the given component as active admin and profile" +
7438dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy                " owner for an existing user.\n" +
7538dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy                "\n" +
7638dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy                "dpm remove-active-admin: Disables an active admin, the admin must have declared" +
7738dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy                " android:testOnly in the application in its manifest. This will also remove" +
7838dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy                " device and profile owners\n");
79b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    }
80b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
81b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    @Override
82b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    public void onRun() throws Exception {
83b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera        mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
84b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
85b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera        if (mDevicePolicyManager == null) {
86b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera            showError("Error: Could not access the Device Policy Manager. Is the system running?");
87b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera            return;
88b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera        }
89b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
90b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera        String command = nextArgRequired();
91b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera        switch (command) {
92e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban            case COMMAND_SET_ACTIVE_ADMIN:
93e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban                runSetActiveAdmin();
94e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban                break;
95b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera            case COMMAND_SET_DEVICE_OWNER:
9641de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera                runSetDeviceOwner();
9741de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera                break;
9841de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera            case COMMAND_SET_PROFILE_OWNER:
9941de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera                runSetProfileOwner();
100b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera                break;
10138dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy            case COMMAND_REMOVE_ACTIVE_ADMIN:
10238dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy                runRemoveActiveAdmin();
10338dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy                break;
104b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera            default:
1059c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera                throw new IllegalArgumentException ("unknown command '" + command + "'");
106b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera        }
107b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    }
108b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera
1096bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov    private void parseArgs(boolean canHaveName) {
110a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki        String opt;
111a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki        while ((opt = nextOption()) != null) {
1126bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov            if ("--user".equals(opt)) {
1136bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                String arg = nextArgRequired();
1146bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                if ("current".equals(arg) || "cur".equals(arg)) {
1156bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                    mUserId = UserHandle.USER_CURRENT;
1166bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                } else {
1176bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                    mUserId = parseInt(arg);
1186bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                }
1196bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                if (mUserId == UserHandle.USER_CURRENT) {
120dc589ac82b5fe2063f4cfd94c8ae26d43d5420a0Sudheer Shanka                    IActivityManager activityManager = ActivityManager.getService();
1216bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                    try {
1226bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                        mUserId = activityManager.getCurrentUser().id;
1236bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                    } catch (RemoteException e) {
1246bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                        e.rethrowAsRuntimeException();
1256bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                    }
1266bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov                }
127a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki            } else if (canHaveName && "--name".equals(opt)) {
128a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki                mName = nextArgRequired();
129a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki            } else {
130a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki                throw new IllegalArgumentException("Unknown option: " + opt);
131a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki            }
132e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban        }
133a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki        mComponent = parseComponentName(nextArgRequired());
134e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban    }
135e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban
136e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban    private void runSetActiveAdmin() throws RemoteException {
1376bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov        parseArgs(/*canHaveName=*/ false);
138e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban        mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
139e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban
140e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban        System.out.println("Success: Active admin set to component " + mComponent.toShortString());
141e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban    }
142e9119876a21575ee7a7d058da85d04c4c4b8a971Zoltan Szatmary-Ban
14341de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera    private void runSetDeviceOwner() throws RemoteException {
1446bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov        parseArgs(/*canHaveName=*/ true);
14558b684f1cdc52467b71c42cfae18433a19ce7d0bMakoto Onuki        mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
1469c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera
1479c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera        try {
148a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki            if (!mDevicePolicyManager.setDeviceOwner(mComponent, mName, mUserId)) {
14941de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera                throw new RuntimeException(
150a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki                        "Can't set package " + mComponent + " as device owner.");
1519c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera            }
1529c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera        } catch (Exception e) {
1539c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera            // Need to remove the admin that we just added.
15458b684f1cdc52467b71c42cfae18433a19ce7d0bMakoto Onuki            mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
1559c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera            throw e;
1569c17388cff98fba9679ce6457ac5003a660c6013Esteban Talavera        }
157afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadway
158afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadway        mDevicePolicyManager.setUserProvisioningState(
159afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadway                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
160afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadway
161a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki        System.out.println("Success: Device owner set to package " + mComponent);
16258b684f1cdc52467b71c42cfae18433a19ce7d0bMakoto Onuki        System.out.println("Active admin set to component " + mComponent.toShortString());
163b5ef162129ced7e9636052af6b7f08d677a01f13Esteban Talavera    }
16441de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera
16538dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy    private void runRemoveActiveAdmin() throws RemoteException {
16638dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy        parseArgs(/*canHaveName=*/ false);
16738dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy        mDevicePolicyManager.forceRemoveActiveAdmin(mComponent, mUserId);
16838dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy        System.out.println("Success: Admin removed " + mComponent);
16938dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy    }
17038dcca51a739d3273b24e84ba5e89e505f45960cKenny Guy
17141de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera    private void runSetProfileOwner() throws RemoteException {
1726bccb76a64ba74c2e0267db1d2ec14a4ffa96771Fyodor Kupolov        parseArgs(/*canHaveName=*/ true);
173474c7aceae0c6a91049deb7db944cbe85b56bcc7Sudheer Shanka        mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
17441de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera
17541de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        try {
176a52562ca9a4144cf30e6d5c6ffe856cc8e284464Makoto Onuki            if (!mDevicePolicyManager.setProfileOwner(mComponent, mName, mUserId)) {
177474c7aceae0c6a91049deb7db944cbe85b56bcc7Sudheer Shanka                throw new RuntimeException("Can't set component " + mComponent.toShortString() +
178474c7aceae0c6a91049deb7db944cbe85b56bcc7Sudheer Shanka                        " as profile owner for user " + mUserId);
17941de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera            }
18041de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        } catch (Exception e) {
18141de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera            // Need to remove the admin that we just added.
182474c7aceae0c6a91049deb7db944cbe85b56bcc7Sudheer Shanka            mDevicePolicyManager.removeActiveAdmin(mComponent, mUserId);
18341de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera            throw e;
18441de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        }
185afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadway
186afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadway        mDevicePolicyManager.setUserProvisioningState(
187afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadway                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
188afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadway
189c48bca50fa64765d3019b0bf82511e030cabcc7aEsteban Talavera        System.out.println("Success: Active admin and profile owner set to "
190474c7aceae0c6a91049deb7db944cbe85b56bcc7Sudheer Shanka                + mComponent.toShortString() + " for user " + mUserId);
19141de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera    }
19241de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera
19341de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera    private ComponentName parseComponentName(String component) {
19441de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        ComponentName cn = ComponentName.unflattenFromString(component);
19541de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        if (cn == null) {
19641de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera            throw new IllegalArgumentException ("Invalid component " + component);
19741de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        }
19841de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        return cn;
19941de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera    }
20041de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera
20141de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera    private int parseInt(String argument) {
20241de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        try {
20341de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera            return Integer.parseInt(argument);
20441de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        } catch (NumberFormatException e) {
20541de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera            throw new IllegalArgumentException ("Invalid integer argument '" + argument + "'", e);
20641de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera        }
20741de9bb6fda113f7cfb8b7b8d64d07d3809f8f20Esteban Talavera    }
208afad8783699b1ba6f3c7ee5961d6ddc2bd771dc1Alan Treadway}
209