0201-APP-Device-Admin

本文介绍如何使用 DeviceAdminSample 应用程序管理手机权限、设置密码、锁屏时间及进行安全操作,包括启用、禁用设备管理员、设置密码质量、长度、最大失败密码次数、强制锁屏、清除数据及调整锁屏超时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过DevicePolicyManager 添加修改手机修改的权限,设置手机开启的密码,设置手机重启的密码,设置手机锁屏的时间,强制锁屏,回复出场设置,中间用到了ActivityManager.isUserAMonkey判断是否是monkey runer进行的随机测试。

DeviceAdminSample.java

/*
 * Copyright (C) 2010 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.example.android.apis.app;

import com.example.android.apis.R;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.AdapterView.OnItemSelectedListener;

/**
 * Example of a do-nothing admin class.  When enabled, it lets you control
 * some of its policy and reports when there is interesting activity.
 */
public class DeviceAdminSample extends DeviceAdminReceiver {

    static SharedPreferences getSamplePreferences(Context context) {
        return context.getSharedPreferences(DeviceAdminReceiver.class.getName(), 0);
    }

    static String PREF_PASSWORD_QUALITY = "password_quality";
    static String PREF_PASSWORD_LENGTH = "password_length";
    static String PREF_MAX_FAILED_PW = "max_failed_pw";

    void showToast(Context context, CharSequence msg) {
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onEnabled(Context context, Intent intent) {
        showToast(context, "Sample Device Admin: enabled");
    }

    @Override
    public CharSequence onDisableRequested(Context context, Intent intent) {
        return "This is an optional message to warn the user about disabling.";
    }

    @Override
    public void onDisabled(Context context, Intent intent) {
        showToast(context, "Sample Device Admin: disabled");
    }

    @Override
    public void onPasswordChanged(Context context, Intent intent) {
        showToast(context, "Sample Device Admin: pw changed");
    }

    @Override
    public void onPasswordFailed(Context context, Intent intent) {
        showToast(context, "Sample Device Admin: pw failed");
    }

    @Override
    public void onPasswordSucceeded(Context context, Intent intent) {
        showToast(context, "Sample Device Admin: pw succeeded");
    }

    /**
     * <p>UI control for the sample device admin.  This provides an interface
     * to enable, disable, and perform other operations with it to see
     * their effect.</p>
     *
     * <p>Note that this is implemented as an inner class only keep the sample
     * all together; typically this code would appear in some separate class.
     */
    public static class Controller extends Activity {
        static final int RESULT_ENABLE = 1;

        DevicePolicyManager mDPM;
        ActivityManager mAM;
        ComponentName mDeviceAdminSample;

        Button mEnableButton;
        Button mDisableButton;

        // Password quality spinner choices
        // This list must match the list found in samples/ApiDemos/res/values/arrays.xml
        final static int mPasswordQualityValues[] = new int[] {
            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
            DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
            DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
            DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
        };
        Spinner mPasswordQuality;
        EditText mPasswordLength;
        Button mSetPasswordButton;

        EditText mPassword;
        Button mResetPasswordButton;

        EditText mMaxFailedPw;

        Button mForceLockButton;
        Button mWipeDataButton;

        private Button mTimeoutButton;

        private EditText mTimeout;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE);
            mAM = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
            mDeviceAdminSample = new ComponentName(Controller.this, DeviceAdminSample.class);

            setContentView(R.layout.device_admin_sample);

            // Watch for button clicks.
            mEnableButton = (Button)findViewById(R.id.enable);
            mEnableButton.setOnClickListener(mEnableListener);
            mDisableButton = (Button)findViewById(R.id.disable);
            mDisableButton.setOnClickListener(mDisableListener);

            mPasswordQuality = (Spinner)findViewById(R.id.password_quality);
            ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
                    this, R.array.password_qualities, android.R.layout.simple_spinner_item);
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            mPasswordQuality.setAdapter(adapter);
            mPasswordQuality.setOnItemSelectedListener(
                    new OnItemSelectedListener() {
                        public void onItemSelected(
                                AdapterView<?> parent, View view, int position, long id) {
                            setPasswordQuality(mPasswordQualityValues[position]);
                        }

                        public void onNothingSelected(AdapterView<?> parent) {
                            setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
                        }
                    });
            mPasswordLength = (EditText)findViewById(R.id.password_length);
            mPasswordLength.addTextChangedListener(new TextWatcher() {
                public void afterTextChanged(Editable s) {
                }
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                }
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    try {
                        setPasswordLength(Integer.parseInt(s.toString()));
                    } catch (NumberFormatException e) {
                    }
                }
            });
            mSetPasswordButton = (Button)findViewById(R.id.set_password);
            mSetPasswordButton.setOnClickListener(mSetPasswordListener);

            mPassword = (EditText)findViewById(R.id.password);
            mResetPasswordButton = (Button)findViewById(R.id.reset_password);
            mResetPasswordButton.setOnClickListener(mResetPasswordListener);

            mMaxFailedPw = (EditText)findViewById(R.id.max_failed_pw);
            mMaxFailedPw.addTextChangedListener(new TextWatcher() {
                public void afterTextChanged(Editable s) {
                }
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                }
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    try {
                        int maxFailCount = Integer.parseInt(s.toString());
                        if (maxFailCount > 0) {
                            Toast.makeText(Controller.this, "WARNING: Phone will wipe after " +
                                    s + " incorrect passwords", Toast.LENGTH_SHORT).show();
                        }
                        setMaxFailedPw(maxFailCount);
                    } catch (NumberFormatException e) {
                    }
                }
            });

            mForceLockButton = (Button)findViewById(R.id.force_lock);
            mForceLockButton.setOnClickListener(mForceLockListener);
            mWipeDataButton = (Button)findViewById(R.id.wipe_data);
            mWipeDataButton.setOnClickListener(mWipeDataListener);

            mTimeout = (EditText) findViewById(R.id.timeout);
            mTimeoutButton = (Button) findViewById(R.id.set_timeout);
            mTimeoutButton.setOnClickListener(mSetTimeoutListener);
        }

        void updateButtonStates() {
            boolean active = mDPM.isAdminActive(mDeviceAdminSample);
            if (active) {
                mEnableButton.setEnabled(false);
                mDisableButton.setEnabled(true);
                mPasswordQuality.setEnabled(true);
                mPasswordLength.setEnabled(true);
                mSetPasswordButton.setEnabled(true);
                mPassword.setEnabled(true);
                mResetPasswordButton.setEnabled(true);
                mForceLockButton.setEnabled(true);
                mWipeDataButton.setEnabled(true);
            } else {
                mEnableButton.setEnabled(true);
                mDisableButton.setEnabled(false);
                mPasswordQuality.setEnabled(false);
                mPasswordLength.setEnabled(false);
                mSetPasswordButton.setEnabled(false);
                mPassword.setEnabled(false);
                mResetPasswordButton.setEnabled(false);
                mForceLockButton.setEnabled(false);
                mWipeDataButton.setEnabled(false);
            }
        }

        void updateControls() {
            SharedPreferences prefs = getSamplePreferences(this);
            final int pwQuality = prefs.getInt(PREF_PASSWORD_QUALITY,
                    DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
            final int pwLength = prefs.getInt(PREF_PASSWORD_LENGTH, 0);
            final int maxFailedPw = prefs.getInt(PREF_MAX_FAILED_PW, 0);

            for (int i=0; i<mPasswordQualityValues.length; i++) {
                if (mPasswordQualityValues[i] == pwQuality) {
                    mPasswordQuality.setSelection(i);
                }
            }
            mPasswordLength.setText(Integer.toString(pwLength));
            mMaxFailedPw.setText(Integer.toString(maxFailedPw));
        }

        void updatePolicies() {
            SharedPreferences prefs = getSamplePreferences(this);
            final int pwQuality = prefs.getInt(PREF_PASSWORD_QUALITY,
                    DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
            final int pwLength = prefs.getInt(PREF_PASSWORD_LENGTH, 0);
            final int maxFailedPw = prefs.getInt(PREF_MAX_FAILED_PW, 0);

            boolean active = mDPM.isAdminActive(mDeviceAdminSample);
            if (active) {
                mDPM.setPasswordQuality(mDeviceAdminSample, pwQuality);
                mDPM.setPasswordMinimumLength(mDeviceAdminSample, pwLength);
                mDPM.setMaximumFailedPasswordsForWipe(mDeviceAdminSample, maxFailedPw);
            }
        }

        void setPasswordQuality(int quality) {
            SharedPreferences prefs = getSamplePreferences(this);
            prefs.edit().putInt(PREF_PASSWORD_QUALITY, quality).commit();
            updatePolicies();
        }

        void setPasswordLength(int length) {
            SharedPreferences prefs = getSamplePreferences(this);
            prefs.edit().putInt(PREF_PASSWORD_LENGTH, length).commit();
            updatePolicies();
        }

        void setMaxFailedPw(int length) {
            SharedPreferences prefs = getSamplePreferences(this);
            prefs.edit().putInt(PREF_MAX_FAILED_PW, length).commit();
            updatePolicies();
        }

        @Override
        protected void onResume() {
            super.onResume();
            updateButtonStates();
        }

        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            switch (requestCode) {
                case RESULT_ENABLE:
                    if (resultCode == Activity.RESULT_OK) {
                        Log.i("DeviceAdminSample", "Admin enabled!");
                    } else {
                        Log.i("DeviceAdminSample", "Admin enable FAILED!");
                    }
                    return;
            }

            super.onActivityResult(requestCode, resultCode, data);
        }

        private OnClickListener mEnableListener = new OnClickListener() {
            public void onClick(View v) {
                // Launch the activity to have the user enable our admin.
                Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
                intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
                        mDeviceAdminSample);
                intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
                        "Additional text explaining why this needs to be added.");
                startActivityForResult(intent, RESULT_ENABLE);
            }
        };

        private OnClickListener mDisableListener = new OnClickListener() {
            public void onClick(View v) {
                mDPM.removeActiveAdmin(mDeviceAdminSample);
                updateButtonStates();
            }
        };

        private OnClickListener mSetPasswordListener = new OnClickListener() {
            public void onClick(View v) {
                // Launch the activity to have the user set a new password.
                Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
                startActivity(intent);
            }
        };

        private OnClickListener mResetPasswordListener = new OnClickListener() {
            public void onClick(View v) {
                if (mAM.isUserAMonkey()) {
                    // Don't trust monkeys to do the right thing!
                    AlertDialog.Builder builder = new AlertDialog.Builder(Controller.this);
                    builder.setMessage("You can't reset my password because you are a monkey!");
                    builder.setPositiveButton("I admit defeat", null);
                    builder.show();
                    return;
                }
                boolean active = mDPM.isAdminActive(mDeviceAdminSample);
                if (active) {
                    mDPM.resetPassword(mPassword.getText().toString(),
                            DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY);
                }
            }
        };

        private OnClickListener mForceLockListener = new OnClickListener() {
            public void onClick(View v) {
                if (mAM.isUserAMonkey()) {
                    // Don't trust monkeys to do the right thing!
                    AlertDialog.Builder builder = new AlertDialog.Builder(Controller.this);
                    builder.setMessage("You can't lock my screen because you are a monkey!");
                    builder.setPositiveButton("I admit defeat", null);
                    builder.show();
                    return;
                }
                boolean active = mDPM.isAdminActive(mDeviceAdminSample);
                if (active) {
                    mDPM.lockNow();
                }
            }
        };

        private OnClickListener mWipeDataListener = new OnClickListener() {
            public void onClick(View v) {
                if (mAM.isUserAMonkey()) {
                    // Don't trust monkeys to do the right thing!
                    AlertDialog.Builder builder = new AlertDialog.Builder(Controller.this);
                    builder.setMessage("You can't wipe my data because you are a monkey!");
                    builder.setPositiveButton("I admit defeat", null);
                    builder.show();
                    return;
                }
                AlertDialog.Builder builder = new AlertDialog.Builder(Controller.this);
                builder.setMessage("This will erase all of your data.  Are you sure?");
                builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        AlertDialog.Builder builder = new AlertDialog.Builder(Controller.this);
                        builder.setMessage("This is not a test.  "
                                + "This WILL erase all of your data!  "
                                + "Are you really absolutely sure?");
                        builder.setPositiveButton("BOOM!", new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                boolean active = mDPM.isAdminActive(mDeviceAdminSample);
                                if (active) {
                                    mDPM.wipeData(0);
                                }
                            }
                        });
                        builder.setNegativeButton("Oops, run away!", null);
                        builder.show();
                    }
                });
                builder.setNegativeButton("No way!", null);
                builder.show();
            }
        };

        private OnClickListener mSetTimeoutListener = new OnClickListener() {

            public void onClick(View v) {
                if (mAM.isUserAMonkey()) {
                    // Don't trust monkeys to do the right thing!
                    AlertDialog.Builder builder = new AlertDialog.Builder(Controller.this);
                    builder.setMessage("You can't lock my screen because you are a monkey!");
                    builder.setPositiveButton("I admit defeat", null);
                    builder.show();
                    return;
                }
                boolean active = mDPM.isAdminActive(mDeviceAdminSample);
                if (active) {
                    long timeMs = 1000L*Long.parseLong(mTimeout.getText().toString());
                    mDPM.setMaximumTimeToLock(mDeviceAdminSample, timeMs);
                }
            }
        };
    }
}


device_admin_sample.xml layot文件

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 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.
-->

<!-- Demonstrates implementation of a DeviceAdmin. -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:padding="4dip"
    android:gravity="center_horizontal"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent" android:layout_height="wrap_content"
        android:layout_weight="0"
        android:paddingBottom="4dip"
        android:text="@string/sample_device_admin_summary"/>

    <LinearLayout android:orientation="horizontal" android:gravity="center"
        android:layout_width="match_parent" android:layout_height="wrap_content">

        <Button android:id="@+id/enable"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="@string/enable_admin">
            <requestFocus />
        </Button>

        <Button android:id="@+id/disable"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="@string/disable_admin">
        </Button>

    </LinearLayout>

    <LinearLayout android:orientation="horizontal" android:gravity="center"
        android:layout_width="match_parent" android:layout_height="wrap_content">

        <Spinner android:id="@+id/password_quality"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawSelectorOnTop="true"
            android:prompt="@string/password_quality">
        </Spinner>

        <EditText android:id="@+id/password_length"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="@string/password_length_hint"
            android:inputType="number">
        </EditText>

    </LinearLayout>

    <Button android:id="@+id/set_password"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android_layout_gravity="east|center_vertical"
        android:text="@string/set_password">
    </Button>

    <LinearLayout android:orientation="horizontal" android:gravity="center"
        android:layout_width="match_parent" android:layout_height="wrap_content">

        <EditText android:id="@+id/password"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="@string/password_hint"
            android:freezesText="true">
        </EditText>

        <Button android:id="@+id/reset_password"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:layout_weight="0"
            android:text="@string/reset_password">
        </Button>

    </LinearLayout>

    <LinearLayout android:orientation="horizontal" android:gravity="center"
        android:layout_width="match_parent" android:layout_height="wrap_content">

        <EditText android:id="@+id/max_failed_pw"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="@string/max_failed_pw_hint"
            android:inputType="number">
        </EditText>

    </LinearLayout>

    <LinearLayout android:orientation="horizontal" android:gravity="center"
        android:layout_width="match_parent" android:layout_height="wrap_content">

        <Button android:id="@+id/force_lock"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:layout_weight="0"
            android:text="@string/force_lock">
        </Button>

        <Button android:id="@+id/wipe_data"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:layout_weight="0"
            android:text="@string/wipe_data">
        </Button>

    </LinearLayout>

    <LinearLayout android:orientation="horizontal" android:gravity="center"
        android:layout_width="match_parent" android:layout_height="wrap_content">

        <EditText android:id="@+id/timeout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="@string/timeout_hint"
            android:inputType="number"
            android:freezesText="true">
        </EditText>

        <Button android:id="@+id/set_timeout"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:layout_weight="0"
            android:text="@string/set_timeout_label">
        </Button>

    </LinearLayout>

</LinearLayout>





<think>好的,我现在需要帮助用户查找与Android设备管理相关的权限或广播动作,特别是关于android DEVICE_ADMIN_ENABLED的广播动作和设备管理权限。 首先,我记得Android的设备管理涉及到设备管理员(Device Admin)功能。设备管理员应用可以执行一些系统级别的操作,比如设置密码策略、远程擦除数据等。通常,这些功能需要特定的权限和广播动作。 用户提到DEVICE_ADMIN_ENABLED,这可能是一个广播动作,用于当设备管理员被启用或禁用时发送。我需要确认这个动作的具体名称,通常是类似DeviceAdminReceiver中的某个常量。 接下来是权限部分。设备管理相关的权限可能包括BIND_DEVICE_ADMIN,这个权限用于声明应用需要绑定设备管理员服务。另外,用户可能需要使用DevicePolicyManager来管理设备策略。 然后,关于广播接收器的注册。设备管理相关的广播可能需要动态注册或者在清单中声明,同时可能需要相应的权限保护,比如android.permission.BIND_DEVICE_ADMIN。 还需要检查是否有相关的Intent动作,比如ACTION_DEVICE_ADMIN_ENABLED,这可能在DeviceAdminReceiver中定义。当设备管理员被启用时,系统会发送这个广播,应用可以接收并处理。 另外,用户可能需要了解如何声明设备管理员组件,即在AndroidManifest.xml中注册一个继承自DeviceAdminReceiver的广播接收器,并申请必要的权限。 最后,生成相关问题的时候,需要考虑用户可能的后续疑问,比如如何实现设备管理员功能、权限申请步骤或如何处理广播接收等。 总结步骤: 1. 确定DEVICE_ADMIN_ENABLED对应的广播动作。 2. 查找设备管理所需的权限,如BIND_DEVICE_ADMIN。 3. 说明如何注册广播接收器和配置清单。 4. 提供示例代码和配置。 5. 生成相关问题,帮助用户深入理解。</think>关于Android设备管理相关的权限和广播动作,您需要关注以下内容: 1. **设备管理广播动作** - **ACTION_DEVICE_ADMIN_ENABLED**:当设备管理员被成功启用时触发的广播,对应常量值为`android.app.action.DEVICE_ADMIN_ENABLED`[^1]。 - **DEVICE_ADMIN_DISABLE_REQUESTED**:当用户尝试禁用设备管理员时触发。 2. **关键权限** - `android.permission.BIND_DEVICE_ADMIN`:必须声明在`<receiver>`标签中,用于绑定设备管理员服务[^2]。 - `android.permission.MANAGE_DEVICE_ADMINS`:系统级权限,仅系统应用可用。 3. **实现步骤** ```xml <!-- AndroidManifest.xml声明示例 --> <receiver android:name=".MyDeviceAdminReceiver" android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="android.app.device_admin" android:resource="@xml/device_admin_policies"/> <intent-filter> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> ``` 4. **设备策略声明** 需在`res/xml/device_admin_policies.xml`中声明支持的策略: ```xml <device-admin xmlns:android="http://schemas.android.com/apk/res/android"> <uses-policies> <limit-password /> <wipe-data /> </uses-policies> </device-admin> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值