OSDN Git Service

Restore legacy VPN stats dialog.
authorJeff Davidson <jpd@google.com>
Fri, 22 Aug 2014 20:05:43 +0000 (13:05 -0700)
committerJeff Davidson <jpd@google.com>
Sat, 23 Aug 2014 00:06:37 +0000 (17:06 -0700)
Was originally removed in ag/522961, but restoring to keep legacy VPN
behavior the same from within VpnSettings. This dialog is only
accesible from VpnSettings and so should only ever be shown for legacy
VPNs.

Bug: 17164793
Change-Id: I06c4e136e1023b8f84edfd15a15264d2e41d325b

core/java/com/android/internal/net/LegacyVpnInfo.java
core/java/com/android/internal/net/VpnConfig.java
packages/VpnDialogs/AndroidManifest.xml
packages/VpnDialogs/res/layout/manage.xml [new file with mode: 0644]
packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java [new file with mode: 0644]
services/core/java/com/android/server/connectivity/Vpn.java

index f812ad6..d6f6d0b 100644 (file)
@@ -40,6 +40,7 @@ public class LegacyVpnInfo implements Parcelable {
 
     public String key;
     public int state = -1;
+    public PendingIntent intent;
 
     @Override
     public int describeContents() {
@@ -50,6 +51,7 @@ public class LegacyVpnInfo implements Parcelable {
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(key);
         out.writeInt(state);
+        out.writeParcelable(intent, flags);
     }
 
     public static final Parcelable.Creator<LegacyVpnInfo> CREATOR =
@@ -59,6 +61,7 @@ public class LegacyVpnInfo implements Parcelable {
             LegacyVpnInfo info = new LegacyVpnInfo();
             info.key = in.readString();
             info.state = in.readInt();
+            info.intent = in.readParcelable(null);
             return info;
         }
 
index aa66d7d..9c3f419 100644 (file)
@@ -28,6 +28,7 @@ import android.net.LinkAddress;
 import android.net.RouteInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
@@ -57,6 +58,15 @@ public class VpnConfig implements Parcelable {
         return intent;
     }
 
+    /** NOTE: This should only be used for legacy VPN. */
+    public static PendingIntent getIntentForStatusPanel(Context context) {
+        Intent intent = new Intent();
+        intent.setClassName(DIALOGS_PACKAGE, DIALOGS_PACKAGE + ".ManageDialog");
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY |
+                Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        return PendingIntent.getActivityAsUser(context, 0, intent, 0, null, UserHandle.CURRENT);
+    }
+
     public static CharSequence getVpnLabel(Context context, String packageName)
             throws NameNotFoundException {
         PackageManager pm = context.getPackageManager();
index 1768400..03d920a 100644 (file)
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
+
+        <activity android:name=".ManageDialog"
+                android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+                android:noHistory="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/packages/VpnDialogs/res/layout/manage.xml b/packages/VpnDialogs/res/layout/manage.xml
new file mode 100644 (file)
index 0000000..6b504e4
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source 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.
+-->
+
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="3mm"
+        android:stretchColumns="0,1"
+        android:shrinkColumns="1">
+
+    <TableRow>
+        <TextView android:text="@string/session" style="@style/label"/>
+        <TextView android:id="@+id/session" style="@style/value"/>
+    </TableRow>
+
+    <TableRow>
+        <TextView android:text="@string/duration" style="@style/label"/>
+        <TextView android:id="@+id/duration" style="@style/value"/>
+    </TableRow>
+
+    <TableRow android:id="@+id/data_transmitted_row" android:visibility="gone">
+        <TextView android:text="@string/data_transmitted" style="@style/label"/>
+        <TextView android:id="@+id/data_transmitted" style="@style/value"/>
+    </TableRow>
+
+    <TableRow android:id="@+id/data_received_row" android:visibility="gone">
+        <TextView android:text="@string/data_received" style="@style/label"/>
+        <TextView android:id="@+id/data_received" style="@style/value"/>
+    </TableRow>
+
+</TableLayout>
+
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
new file mode 100644 (file)
index 0000000..cc8500a
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2011 The Android Open Source 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 com.android.vpndialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.IConnectivityManager;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.net.VpnConfig;
+
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+
+public class ManageDialog extends AlertActivity implements
+        DialogInterface.OnClickListener, Handler.Callback {
+    private static final String TAG = "VpnManage";
+
+    private VpnConfig mConfig;
+
+    private IConnectivityManager mService;
+
+    private TextView mDuration;
+    private TextView mDataTransmitted;
+    private TextView mDataReceived;
+    private boolean mDataRowsHidden;
+
+    private Handler mHandler;
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if (getCallingPackage() != null) {
+            Log.e(TAG, getCallingPackage() + " cannot start this activity");
+            finish();
+            return;
+        }
+
+        try {
+
+            mService = IConnectivityManager.Stub.asInterface(
+                    ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+
+            mConfig = mService.getVpnConfig();
+
+            // mConfig can be null if we are a restricted user, in that case don't show this dialog
+            if (mConfig == null) {
+                finish();
+                return;
+            }
+
+            View view = View.inflate(this, R.layout.manage, null);
+            if (mConfig.session != null) {
+                ((TextView) view.findViewById(R.id.session)).setText(mConfig.session);
+            }
+            mDuration = (TextView) view.findViewById(R.id.duration);
+            mDataTransmitted = (TextView) view.findViewById(R.id.data_transmitted);
+            mDataReceived = (TextView) view.findViewById(R.id.data_received);
+            mDataRowsHidden = true;
+
+            if (mConfig.legacy) {
+                mAlertParams.mTitle = getText(R.string.legacy_title);
+            } else {
+                mAlertParams.mTitle = VpnConfig.getVpnLabel(this, mConfig.user);
+            }
+            if (mConfig.configureIntent != null) {
+                mAlertParams.mPositiveButtonText = getText(R.string.configure);
+                mAlertParams.mPositiveButtonListener = this;
+            }
+            mAlertParams.mNeutralButtonText = getText(R.string.disconnect);
+            mAlertParams.mNeutralButtonListener = this;
+            mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
+            mAlertParams.mNegativeButtonListener = this;
+            mAlertParams.mView = view;
+            setupAlert();
+
+            if (mHandler == null) {
+                mHandler = new Handler(this);
+            }
+            mHandler.sendEmptyMessage(0);
+        } catch (Exception e) {
+            Log.e(TAG, "onResume", e);
+            finish();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (!isFinishing()) {
+            finish();
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        try {
+            if (which == DialogInterface.BUTTON_POSITIVE) {
+                mConfig.configureIntent.send();
+            } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+                if (mConfig.legacy) {
+                    mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+                } else {
+                    mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "onClick", e);
+            finish();
+        }
+    }
+
+    @Override
+    public boolean handleMessage(Message message) {
+        mHandler.removeMessages(0);
+
+        if (!isFinishing()) {
+            if (mConfig.startTime != -1) {
+                long seconds = (SystemClock.elapsedRealtime() - mConfig.startTime) / 1000;
+                mDuration.setText(String.format("%02d:%02d:%02d",
+                        seconds / 3600, seconds / 60 % 60, seconds % 60));
+            }
+
+            String[] numbers = getNumbers();
+            if (numbers != null) {
+                // First unhide the related data rows.
+                if (mDataRowsHidden) {
+                    findViewById(R.id.data_transmitted_row).setVisibility(View.VISIBLE);
+                    findViewById(R.id.data_received_row).setVisibility(View.VISIBLE);
+                    mDataRowsHidden = false;
+                }
+
+                // [1] and [2] are received data in bytes and packets.
+                mDataReceived.setText(getString(R.string.data_value_format,
+                        numbers[1], numbers[2]));
+
+                // [9] and [10] are transmitted data in bytes and packets.
+                mDataTransmitted.setText(getString(R.string.data_value_format,
+                        numbers[9], numbers[10]));
+            }
+            mHandler.sendEmptyMessageDelayed(0, 1000);
+        }
+        return true;
+    }
+
+    private String[] getNumbers() {
+        DataInputStream in = null;
+        try {
+            // See dev_seq_printf_stats() in net/core/dev.c.
+            in = new DataInputStream(new FileInputStream("/proc/net/dev"));
+            String prefix = mConfig.interfaze + ':';
+
+            while (true) {
+                String line = in.readLine().trim();
+                if (line.startsWith(prefix)) {
+                    String[] numbers = line.substring(prefix.length()).split(" +");
+                    for (int i = 1; i < 17; ++i) {
+                        if (!numbers[i].equals("0")) {
+                            return numbers;
+                        }
+                    }
+                    break;
+                }
+            }
+        } catch (Exception e) {
+            // ignore
+        } finally {
+            try {
+                in.close();
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+        return null;
+    }
+}
index 69caab9..94aa421 100644 (file)
@@ -22,6 +22,7 @@ import static android.system.OsConstants.AF_INET6;
 
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -105,6 +106,7 @@ public class Vpn {
     private boolean mAllowIPv6;
     private Connection mConnection;
     private LegacyVpnRunner mLegacyVpnRunner;
+    private PendingIntent mStatusIntent;
     private volatile boolean mEnableTeardown = true;
     private final IConnectivityManager mConnService;
     private final INetworkManagementService mNetd;
@@ -237,6 +239,7 @@ public class Vpn {
 
         // Reset the interface.
         if (mInterface != null) {
+            mStatusIntent = null;
             agentDisconnect();
             jniReset(mInterface);
             mInterface = null;
@@ -567,17 +570,20 @@ public class Vpn {
 
         // add the user
         mVpnUsers.add(UidRange.createForUser(user));
+
+        prepareStatusIntent();
     }
 
     private void removeVpnUserLocked(int user) {
-            if (!isRunningLocked()) {
-                throw new IllegalStateException("VPN is not active");
-            }
-            UidRange uidRange = UidRange.createForUser(user);
-            if (mNetworkAgent != null) {
-                mNetworkAgent.removeUidRanges(new UidRange[] { uidRange });
-            }
-            mVpnUsers.remove(uidRange);
+        if (!isRunningLocked()) {
+            throw new IllegalStateException("VPN is not active");
+        }
+        UidRange uidRange = UidRange.createForUser(user);
+        if (mNetworkAgent != null) {
+            mNetworkAgent.removeUidRanges(new UidRange[] { uidRange });
+        }
+        mVpnUsers.remove(uidRange);
+        mStatusIntent = null;
     }
 
     private void onUserAdded(int userId) {
@@ -645,6 +651,7 @@ public class Vpn {
         public void interfaceRemoved(String interfaze) {
             synchronized (Vpn.this) {
                 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
+                    mStatusIntent = null;
                     mVpnUsers = null;
                     mInterface = null;
                     if (mConnection != null) {
@@ -702,6 +709,15 @@ public class Vpn {
         }
     }
 
+    private void prepareStatusIntent() {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     public synchronized boolean addAddress(String address, int prefixLength) {
         if (Binder.getCallingUid() != mOwnerUID || mInterface == null || mNetworkAgent == null) {
             return false;
@@ -911,6 +927,9 @@ public class Vpn {
         final LegacyVpnInfo info = new LegacyVpnInfo();
         info.key = mConfig.user;
         info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
+        if (mNetworkInfo.isConnected()) {
+            info.intent = mStatusIntent;
+        }
         return info;
     }