精华内容
下载资源
问答
  • adb填充安卓手机的内存
    千次阅读 热门讨论
    2019-02-21 15:12:45

    填充终端手机的内存,如ROM内存,在sdcard里,大家经常都是拷贝东西进去填满,其实不用,一条命令搞定。

    填充1G的内存:

    adb shell dd if=/dev/zero of=sdcard/aa bs=1024000 count=1024

     

    只需要更改两处

    1.修改count大小的值,1G=1024,要填充多少自己计算;

    2.aa是文件名(在sdcard新建个aa文件的大小),可不改,它自己生成一个文件叫aa,大小是1G这样子,但是可以多填写几个文件,但是这个文件名要更改,不然会覆盖掉(注意,命名规则不能在文件管理器存在的,否则新建失败)

     

    成功会有类似如下显示,在去查看剩余内存即可!

    3036001+0 records in

    3036000+0 records out

    1554432000 bytes transferred in 163.839 secs (9487557 bytes/sec)

     

    其实有专门人写APK填充工具,这个有界面可供选择修改内存,更可视化。后续会上传。

     

    ---有不明白的请联系我。

    更多相关内容
  • 智能手机rom填充工具apk

    热门讨论 2013-02-27 13:15:45
    智能手机填内存的工具,很好用,、 rom填充工具,方便测试人员测试所用
  • 简单介绍下: 此工具专为填充通话记录使用,做手机测试的朋友最烦的是填充数据,要一个个填充,很麻烦,通话记录导入Vcard(.vcf)文件也是相当麻烦,使用工具只需连接终端,adb root下即可(本工具是根据当前公司的...

     

    简单介绍下:

           此工具专为填充通话记录使用,做手机测试的朋友最烦的是填充数据,要一个个填充,很麻烦,通话记录导入Vcard(.vcf)文件也是相当麻烦,使用工具只需连接终端,adb root下即可(本工具是根据当前公司的产品和需求制定,可根据源码修改)
          终端安卓系统带的是双系统,普通的不需要此选项,我们自己使用的是公网手机,对讲机一般是专网/PDT,echat是产品的apk可忽略.
          使用前终端需要root,可以cmd界面进行adb root,每个产品root指令不同,也可以使用工具辅助root。

    package toolUI;
    
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.InputStreamReader;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.Statement;
    import java.util.Random;
    import java.util.Scanner;
    
    import org.sqlite.JDBC;
    
    import javax.swing.JButton;
    import javax.swing.JFileChooser;
    import javax.swing.JFrame;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
    import javax.swing.ScrollPaneConstants;
    
    import java.awt.Panel;
    import java.awt.BorderLayout;
    import java.awt.Dialog.ModalExclusionType;
    import java.awt.FlowLayout;
    import javax.swing.JLabel;
    import javax.swing.DropMode;
    import javax.swing.JScrollPane;
    
    public class DBtlloview {
    	//全局变量
    	public JFrame frame;
    //	JPanel container;
    //	private JTextArea textArea;
    //	private JTextField textField_RF;
    //	private JTextField textField_number;
    //	private JTextField textField_name;
    //	private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    	private static JButton btn_Button_xuanzeDBfile=null;
    	private JTextField jTextField;
    //	private static JButton btn_Button_xieru;
    //	private String mTotalContent;//文件内容
    //	private File mDestFile;//目标文件
    //	int ROM;//鼠标指定行数
    //	String sum;//原来的行内容
    	private JTextField textNumberField;
         /**
    	 * Launch the application.启动应用程序
    	 */
    	public static void main(String[] args) throws Exception {
    		EventQueue.invokeLater(new Runnable() {
    			public void run() {
    				try {
    					DBtlloview window = new DBtlloview();
    					window.frame.setVisible(true);
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    			}
    		});
    				
    	 
    	}
    
    
    	/**
    	 * Create the application.初始化
    	 */
    	public DBtlloview() {
    		initialize();
    	}
    
    	/**
    	 * Initialize the contents of the frame.
    	 */
    	private void initialize() {
    
    		frame = new JFrame("享有盛誉之名");
    		frame.setTitle("dsy通话记录填充工具V0.0.1");
    		frame.setBounds(100, 100, 508, 432);
    		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
    		frame.getContentPane().setLayout(null);
    		
    
    		//选择按钮
    		JButton btn_Button_xuanzeDBfile = new JButton("选择文件");
    		btn_Button_xuanzeDBfile.setBounds(14, 32, 95, 22);
    		frame.getContentPane().add(btn_Button_xuanzeDBfile);
    		//文件路径显示框
    		jTextField = new JTextField();
    		jTextField.setText("D:\\");
    		jTextField.setBounds(113, 33, 285, 21);
    		frame.getContentPane().add(jTextField);
    		jTextField.setColumns(10);
    		
    		//文件选择路径实现
    	 	btn_Button_xuanzeDBfile.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent e) {
    				// TODO Auto-generated method stub
    				JFileChooser jChooser = new JFileChooser();
    				jChooser.setDialogTitle("dengshengyu");
    				int returnVal = jChooser.showOpenDialog(null);
    				java.io.File file =jChooser.getSelectedFile();
    				if (JFileChooser.APPROVE_OPTION == returnVal) {
    					//需要传入的参数
    					jTextField.setText(file.getAbsolutePath());
    					String strPath = file.getAbsolutePath();
    					System.out.println(strPath);
    				 }
    			}
        	});
    	 	
    	 	
    	 	JLabel label = new JLabel("填充数量:");
    	 	label.setBounds(34, 80, 75, 18);
    	 	frame.getContentPane().add(label);
    	 	//填充数量框
    	 	textNumberField = new JTextField();
    	 	textNumberField.setText("请输入正确数字");
    		textNumberField.setColumns(10);
    		textNumberField.setBounds(113, 78, 99, 21);
    		frame.getContentPane().add(textNumberField);
    		//只能输入数字
    		textNumberField.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    				if(textNumberField.getText().equals(null)){
    					
    				}
    			}
    			
    		});
    		//填充按钮
    		JButton btn_Button_tianchong = new JButton("填充");
    		btn_Button_tianchong.setBounds(218, 78, 99, 22);
    		frame.getContentPane().add(btn_Button_tianchong);
    		//填充实现
    		btn_Button_tianchong.addActionListener(new ActionListener(){
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    				int Allanmber = Integer.parseInt(textNumberField.getText());//执行次数
    				System.out.println("执行次数:"+Allanmber);
    				int anumber = 1;
    				int res=JOptionPane.showConfirmDialog(null, "若无calllog.db文件请取消", "盛誉提醒", JOptionPane.YES_NO_OPTION);		
    		if(res==JOptionPane.YES_NO_OPTION){		
    	         for(int j=0;j<Allanmber;j++){//执行输入的次数
    				/**
    				 * JDBC数据库连接
    				 * */
    					//转入路径
    					String strPath = jTextField.getText();//db目标路径
    				try {
    					Class.forName("org.sqlite.JDBC");	
    				    Connection conn = DriverManager.getConnection("jdbc:sqlite:"+strPath+"\\calllog.db"); //PC上的
    //					Connection conn = DriverManager.getConnection
    //							("jdbc:sqlite:/data/data/com.android.providers.contacts/databases/calllog.db");				   
    					/*生成随机号码*/
    					Random random = new Random();
    					String number="";
    					for(int i=0;i<6;i++){
    						number+=random.nextInt(10);
    					}
    					System.out.print(number);
    					String phonenumber = number; //手机号码是字符类似
    					System.out.print("phonenumber"+phonenumber);
    					// calls是表名称
    					//严格按照这样的格式编写,数据表的分为字符和数字,红色(为null)和绿色的都是数字类型,白色的都是字符类型
    							//insert into calls values(11,'333333',1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)
    							String YESpublicsql = "insert into calls values("
    							           + anumber + ",'" + phonenumber+"'"
    							           + ",1,'1','1',0,0,0,0,0,'1','1','1',0,-1,1,0,0,0,'CN',0,0,'1','1',0,0,0,0,'1',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)";
    							System.out.print("YESpublicsql:"+YESpublicsql);
    					//insert into calls values(11,'333333',1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)
    					//String sql = "insert into calls values("
    					           + anumber + ",'" + phonenumber+"'"
    					           + ",1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)";
    					System.out.print("sql"+sql);
    					anumber=anumber+1;
    				    
    					System.out.println("SQL语句:"+sql);
    					Statement stmt = conn.createStatement();
    					stmt.executeUpdate(sql);
    				    conn.close();
    
    				} catch (Exception e) {
    					// TODO: handle exception
    					e.printStackTrace();
    				}
    				
    				
    			 }//for
    		  }else{
    			  JOptionPane.showMessageDialog(null, "此路径下无db文件无法操作");//消息框
    		  }
    				
    		}
    			
    		});
    
    		//删除全部
    		JButton btn_Button_DeleteAll = new JButton("全部删除");
    		btn_Button_DeleteAll.setBounds(329, 78, 99, 22);
    		frame.getContentPane().add(btn_Button_DeleteAll);
    		
    
    		//删除实现 DELETE FROM calls WHERE _id = 15
    		btn_Button_DeleteAll.addActionListener(new ActionListener(){
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    //				int n = JOptionPane.showConfirmDialog(null, "你确定要删除全部通话记录?", "删除全部通话记录",
    //						JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); 
    //				Object[] options ={ "确定", "取消" };  //自定义按钮上的文字
    //				int m = JOptionPane.showOptionDialog(null, "再次确认是否要删除全部?", "清空全部通话记录",
    //						JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); 
    				int res=JOptionPane.showConfirmDialog(null, "暂不实现,删除后会导致终端报错", "删除全部通话记录", JOptionPane.YES_NO_OPTION);
                    if(res==JOptionPane.YES_OPTION){
                    	//点击“是”后执行这个代码块
    //    				try {
    //    				 System.out.println("删除全部通讯记录");	
    //    				 Runtime.getRuntime().exec
    //    					("adb shell su -rm -r /data/data/com.android.providers.contacts/databases/calllog.db");
        				 JOptionPane.showMessageDialog(null, "请恢复出厂设置");//消息框
    //    				} catch (Exception e) {
    //    					// TODO: handle exception
    //    					
    //    				}   
                    }else{
                    	//点击“否”后执行这个代码块
                        System.out.println("您已取消删除操作");    
                        return;
                    }
    				
    			  
    			}
    			
    			
    		});
    		
    	
    		/*辅助设计*/
    		JLabel label_1 = new JLabel("进度显示:");
    		label_1.setBounds(14, 194, 95, 18);
    		frame.getContentPane().add(label_1);
    
    		JScrollPane scrollPane = new JScrollPane();
    		scrollPane.setBounds(14, 213, 462, 159);
    		frame.getContentPane().add(scrollPane);
    
    		JTextArea textArea = new JTextArea();
    		scrollPane.setViewportView(textArea);
    		//辅助操作
    		JLabel label_2 = new JLabel("辅助操作(对手机):");
    		label_2.setBounds(14, 127, 162, 29);
    		frame.getContentPane().add(label_2);
    		//root
    		JButton btn_Button_root = new JButton("root手机");
    		btn_Button_root.setBounds(14, 159, 119, 22);
    		frame.getContentPane().add(btn_Button_root);
    		//root功能
    		btn_Button_root.addActionListener(new ActionListener(){
    
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    				try {
    
    //					if(dev == null){
    //					 Runtime.getRuntime().exec("adb wait-for-device");
    	   				 Runtime.getRuntime().exec("adb root xxx.cn");//xxx是该产品的root密码
    	   				 Runtime.getRuntime().exec("adb remount");
    	   				 JOptionPane.showMessageDialog(null, "root成功!");//消息框
    //					 }
    	   				} catch (Exception e) {
    	   					// TODO: handle exception
    	   				JOptionPane.showMessageDialog(null, "请检查端口");//消息框
    	   				}   
    			}
    			
    		});
    		
    		JButton btn_Button_input = new JButton("导入db文件");
    		btn_Button_input.setBounds(292, 159, 119, 22);
    		frame.getContentPane().add(btn_Button_input);
    		//导入功能
    		btn_Button_input.addActionListener(new ActionListener(){
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    			int res=JOptionPane.showConfirmDialog(null, "请确认root和db文件路径选对了吗?", "必须root", JOptionPane.YES_NO_OPTION);
    			if(res==JOptionPane.YES_NO_OPTION){//选择确定执行此操作
    				try {
    					//需要传入文件路径
    					String strPath = jTextField.getText();				
    					System.out.println(strPath);//这个就是路径	
    	   				Runtime.getRuntime().exec("adb push "+strPath+"calllog.db"
    	   						+" /data/data/com.android.providers.contacts/databases/");
    	   				JOptionPane.showMessageDialog(null, "已复制到手机中,重启终端");//消息框
    	   				} catch (Exception e) {
    	   					// TODO: handle exception
    	   				
    	   				}   
    			 }else{
    				 JOptionPane.showMessageDialog(null, "不root无法执行此操作", "警告",JOptionPane.ERROR_MESSAGE);
    
    			 }
    		
    			}
    			
    		});
    		
    		JButton btn_Button_out = new JButton("导出db文件");
    		btn_Button_out.setBounds(147, 159, 131, 22);
    		frame.getContentPane().add(btn_Button_out);
    		//导出功能
    		btn_Button_out.addActionListener(new ActionListener(){
    		 public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    			int res=JOptionPane.showConfirmDialog(null, "请确定已经root和无中文路径", "必须root", JOptionPane.YES_NO_OPTION);
    			if(res==JOptionPane.YES_NO_OPTION){//选择确定执行此操作
    				
    				try {
    					//需要传入文件路径
    					 String strPath = jTextField.getText();				
    					 System.out.println(strPath);//这个就是路径	
    	   				 Runtime.getRuntime().exec(
    	   						 "adb pull /data/data/com.android.providers.contacts/databases/calllog.db "+strPath);
    	   				 JOptionPane.showMessageDialog(null, "已复制至"+strPath+"根目录下");//消息框
    	   				} catch (Exception e) {
    	   					// TODO: handle exception
    	   				 
    	   				}  
    				
    			}else{
    				//选择否弹出警告
    				JOptionPane.showMessageDialog(null, "不root或中文路径无法执行此操作", "盛誉警告",JOptionPane.ERROR_MESSAGE);
    			 }
    
    		 }
    			
    		});
    		
    
    	 	
    	}
    }
    

    package toolUI;
    
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.InputStreamReader;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.Statement;
    import java.util.Random;
    import java.util.Scanner;
    
    import org.sqlite.JDBC;
    
    import javax.swing.ButtonGroup;
    import javax.swing.JButton;
    import javax.swing.JFileChooser;
    import javax.swing.JFrame;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
    import javax.swing.ScrollPaneConstants;
    
    import java.awt.Panel;
    import java.awt.BorderLayout;
    import java.awt.Dialog.ModalExclusionType;
    import java.awt.FlowLayout;
    import javax.swing.JLabel;
    import javax.swing.DropMode;
    import javax.swing.JScrollPane;
    import javax.swing.JMenuBar;
    import javax.swing.JComboBox;
    import javax.swing.DefaultComboBoxModel;
    import java.sql.JDBCType;
    import javax.swing.JRadioButton;
    
    public class DBtlloview {
    	//全局变量
    	public JFrame frame;
    //	JPanel container;
    //	private JTextArea textArea;
    //	private JTextField textField_RF;
    //	private JTextField textField_number;
    //	private JTextField textField_name;
    //	private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    	private static JButton btn_Button_xuanzeDBfile=null;
    	private JTextField jTextField;
    	private JRadioButton rdbtnDouble_YES;
    	private JRadioButton rdbtnRadio_ON;
    	private JRadioButton rdbtnType_public;
    	private JRadioButton rdbtnType_gota;
    	private JRadioButton rdbtnType_echat;
    	private JRadioButton rdbtnType_PDT;
    //	private static JButton btn_Button_xieru;
    //	private String mTotalContent;//文件内容
    //	private File mDestFile;//目标文件
    //	int ROM;//鼠标指定行数
    //	String sum;//原来的行内容
    	private JTextField textNumberField;
         /**
    	 * Launch the application.启动应用程序
    	 */
    	public static void main(String[] args) throws Exception {
    		EventQueue.invokeLater(new Runnable() {
    			public void run() {
    				try {
    					DBtlloview window = new DBtlloview();
    					window.frame.setVisible(true);
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    			}
    		});
    				 
    	}
    
    
    	/**
    	 * Create the application.初始化
    	 */
    	public DBtlloview() {
    		initialize();
    	}
    
    	/**
    	 * Initialize the contents of the frame.
    	 */
    	private void initialize() {
    
    		frame = new JFrame("享有盛誉之名");
    		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    		frame.setTitle("dsy通话记录填充工具V0.0.1");
    		frame.setBounds(100, 100, 566, 684);
    		frame.getContentPane().setLayout(null);
    		
    
    		//选择按钮
    		JButton btn_Button_xuanzeDBfile = new JButton("选择文件");
    		btn_Button_xuanzeDBfile.setBounds(14, 120, 95, 22);
    		frame.getContentPane().add(btn_Button_xuanzeDBfile);
    		//文件路径显示框
    		jTextField = new JTextField();
    		jTextField.setText("D:\\");
    		jTextField.setBounds(131, 120, 285, 21);
    		frame.getContentPane().add(jTextField);
    		jTextField.setColumns(10);
    		
    		//文件选择路径实现
    	 	btn_Button_xuanzeDBfile.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent e) {
    				// TODO Auto-generated method stub
    				JFileChooser jChooser = new JFileChooser();
    				jChooser.setDialogTitle("dengshengyu");
    				int returnVal = jChooser.showOpenDialog(null);
    				java.io.File file =jChooser.getSelectedFile();
    				if (JFileChooser.APPROVE_OPTION == returnVal) {
    					//需要传入的参数
    					jTextField.setText(file.getAbsolutePath());
    					String strPath = file.getAbsolutePath();
    					System.out.println(strPath);
    				 }
    			}
        	});
    	 	
    	 	//询问
    		JLabel label_3 = new JLabel("是否是双系统:");
    		label_3.setBounds(14, 24, 119, 18);
    		frame.getContentPane().add(label_3);
    		
    		rdbtnDouble_YES = new JRadioButton("是");
    		rdbtnDouble_YES.setSelected(true);
    		rdbtnDouble_YES.setBounds(131, 20, 58, 27);
    		frame.getContentPane().add(rdbtnDouble_YES);
    		
    		rdbtnRadio_ON = new JRadioButton("否");
    		rdbtnRadio_ON.setBounds(204, 20, 63, 27);
    		frame.getContentPane().add(rdbtnRadio_ON);
    		//二选一的方法
    		final ButtonGroup group1=new ButtonGroup();
    		group1.add(rdbtnDouble_YES);
    		group1.add(rdbtnRadio_ON);
    
    
    		
    		JLabel label_4 = new JLabel("选择填充类型:");
    		label_4.setBounds(14, 58, 119, 29);
    		frame.getContentPane().add(label_4);
    		
    		rdbtnType_public = new JRadioButton("公网");
    		rdbtnType_public.setSelected(true);
    		rdbtnType_public.setBounds(131, 59, 75, 27);
    		frame.getContentPane().add(rdbtnType_public);
    		
    		rdbtnType_gota = new JRadioButton("专网");
    		rdbtnType_gota.setBounds(204, 59, 75, 27);
    		frame.getContentPane().add(rdbtnType_gota);
    		
    		rdbtnType_echat = new JRadioButton("echat");
    		rdbtnType_echat.setBounds(285, 59, 75, 27);
    		frame.getContentPane().add(rdbtnType_echat);
    		
    		rdbtnType_PDT = new JRadioButton("PDT");
    		rdbtnType_PDT.setBounds(375, 59, 75, 27);
    		frame.getContentPane().add(rdbtnType_PDT);
    		//4选1的方法
    		final ButtonGroup group2=new ButtonGroup();
    		group2.add(rdbtnType_public);
    		group2.add(rdbtnType_gota);
    		group2.add(rdbtnType_echat);
    		group2.add(rdbtnType_PDT);
    		
    	 	
    	 	JLabel label = new JLabel("填充数量:");
    	 	label.setBounds(14, 171, 75, 18);
    	 	frame.getContentPane().add(label);
    	 	//填充数量框
    	 	textNumberField = new JTextField();
    	 	textNumberField.setText("请输入正确数字");
    		textNumberField.setColumns(10);
    		textNumberField.setBounds(91, 169, 99, 21);
    		frame.getContentPane().add(textNumberField);
    		//只能输入数字
    		textNumberField.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    				if(textNumberField.getText().equals(null)){
    					
    				}
    			}
    			
    		});
    		//填充按钮
    		JButton btn_Button_tianchong = new JButton("填充");
    		btn_Button_tianchong.setBounds(204, 169, 99, 22);
    		frame.getContentPane().add(btn_Button_tianchong);
    /*		if(rdbtnDouble_YES.isSelected()==true){
    			System.out.println("选择是");
    			if(rdbtnType_public.isSelected()){
    				System.out.println("选择公网");//公网路径不变
    			}
    			if(rdbtnType_gota.isSelected()){
    				System.out.println("选择专网");
    			}
    			if(rdbtnType_echat.isSelected()){
    				System.out.println("选择echat"); 	
    			}
    			if(rdbtnType_PDT.isSelected()){
    				System.out.println("选择PDT");
    			}
    		}else{
    			System.out.println("选择否");
    			if(rdbtnType_public.isSelected()){
    				System.out.println("选择公网1");
    			}
    			if(rdbtnType_gota.isSelected()){
    				System.out.println("选择专网1");
    			}
    			if(rdbtnType_echat.isSelected()){
    				System.out.println("选择echat1");
    			}
    			if(rdbtnType_PDT.isSelected()){
    				System.out.println("选择PDT1");
    			}
    		}*/
    		//填充实现
    				btn_Button_tianchong.addActionListener(new ActionListener(){
    					public void actionPerformed(ActionEvent arg0) {
    						// TODO Auto-generated method stub
    						int Allanmber = Integer.parseInt(textNumberField.getText());//执行次数
    						System.out.println("执行次数:"+Allanmber);
    						int anumber = 1;
    						int res=JOptionPane.showConfirmDialog(null, "若无calllog.db文件请取消", "盛誉提醒", JOptionPane.YES_NO_OPTION);		
    				if(res==JOptionPane.YES_NO_OPTION){		
    			         for(int j=0;j<Allanmber;j++){//执行输入的次数
    						/**
    						 * JDBC数据库连接
    						 * */
    							//转入路径
    							String strPath = jTextField.getText();//db目标路径
    						try {
    							Class.forName("org.sqlite.JDBC");	
    						    Connection conn = DriverManager.getConnection("jdbc:sqlite:"+strPath+"\\calllog.db"); //PC上的
    //							Connection conn = DriverManager.getConnection
    //									("jdbc:sqlite:/data/data/com.android.providers.contacts/databases/calllog.db");				   
    							/*生成随机号码*/
    							Random random = new Random();
    							String number="";
    							for(int i=0;i<6;i++){
    								number+=random.nextInt(10);
    							}
    							System.out.print(number);
    							String phonenumber = number; //手机号码是字符类似
    							System.out.print("phonenumber"+phonenumber);
    							// calls是表名称
    							//严格按照这样的格式编写,数据表的分为字符和数字,红色(为null)和绿色的都是数字类型,白色的都是字符类型
    							//insert into calls values(11,'333333',1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)
    							//8组
    							String YESpublicsql = "insert into calls values("
    							           + anumber + ",'" + phonenumber+"'"
    							           + ",1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)";
    							System.out.print("YESpublicsql:"+YESpublicsql);
    
    							String YESgotasql = "insert into calls values("
    							           + anumber + ",'" + phonenumber+"'"
    							           + ",1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)";
    							System.out.print("YESgotasql:"+YESgotasql);
    										
    							String YESechatsql = "insert into calls values("
    							           + anumber + ",'" + phonenumber+"'"
    							           + ",1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)";
    							System.out.print("YESechatsql:"+YESechatsql);	
    							
    							String YESPDTsql = "insert into calls values("
    							           + anumber + ",'" + phonenumber+"'"
    							           + ",1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)";
    							System.out.print("YESPDTsql:"+YESPDTsql);
    							
    							String ONpublicsql = "insert into calls values("
    							           + anumber + ",'" + phonenumber+"'"
    							           + ",1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)";
    							System.out.print("ONpublicsql:"+ONpublicsql);
    							
    							String ONgotasql = "insert into calls values("
    							           + anumber + ",'" + phonenumber+"'"
    							           + ",1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)";
    							System.out.print("ONgotasql:"+ONgotasql);
    							
    							String ONechatsql = "insert into calls values("
    							           + anumber + ",'" + phonenumber+"'"
    							           + ",1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)";
    							System.out.print("ONechatsql:"+ONechatsql);
    							
    							String ONPDTsql = "insert into calls values("
    							           + anumber + ",'" + phonenumber+"'"
    							           + ",1,'','',1593485812233,0,null,2,0,'com.android.phone/com.android.services.telephony.TelephonyConnectionService','89860317147559839723','',0,-1,1,null,null,null,'CN',null,null,'','',null,null,0,null,'',1,1593486109999,null,null,null,null,null,null,0,null,0,0,0,0,0,0)";
    							System.out.print("ONPDTsql:"+ONPDTsql);
    							
    							if(rdbtnDouble_YES.isSelected()==true){
    								System.out.println("选择是");
    								if(rdbtnType_public.isSelected()){
    									System.out.println("选择公网");//公网路径不变
    									
    									anumber=anumber+1;    
    									System.out.println("SQL语句:"+YESpublicsql);
    									Statement stmt = conn.createStatement();
    									stmt.executeUpdate(YESpublicsql);
    								    conn.close();
    								}
    								if(rdbtnType_gota.isSelected()){
    									System.out.println("选择专网");
    									JOptionPane.showMessageDialog(null, "请确认安卓P双系统是否有专网?,请联系邓盛誉修改");//消息框
    								}
    								if(rdbtnType_echat.isSelected()){
    									System.out.println("选择echat");
    									
    									anumber=anumber+1;    
    									System.out.println("SQL语句:"+YESpublicsql);
    									Statement stmt = conn.createStatement();
    									stmt.executeUpdate(YESpublicsql);
    								    conn.close();
    								}
    								if(rdbtnType_PDT.isSelected()){
    									System.out.println("选择PDT");
    									JOptionPane.showMessageDialog(null, "请确认安卓P双系统是否有PDT?,请联系邓盛誉修改");//消息框
    								}
    							}else{
    								System.out.println("选择否");
    								if(rdbtnType_public.isSelected()){
    									System.out.println("选择公网1");
    									
    									anumber=anumber+1;    
    									System.out.println("SQL语句:"+ ONpublicsql);
    									Statement stmt = conn.createStatement();
    									stmt.executeUpdate(ONpublicsql);
    								    conn.close();
    								}
    								if(rdbtnType_gota.isSelected()){
    									System.out.println("选择专网1");
    									
    									anumber=anumber+1;    
    									System.out.println("SQL语句:"+ ONgotasql);
    									Statement stmt = conn.createStatement();
    									stmt.executeUpdate(YESpublicsql);
    								    conn.close();
    								}
    								if(rdbtnType_echat.isSelected()){
    									System.out.println("选择echat1");
    									
    									anumber=anumber+1;    
    									System.out.println("SQL语句:"+ ONechatsql);
    									Statement stmt = conn.createStatement();
    									stmt.executeUpdate(YESpublicsql);
    								    conn.close();
    								}
    								if(rdbtnType_PDT.isSelected()){
    									System.out.println("选择PDT1");
    									
    									anumber=anumber+1;    
    									System.out.println("SQL语句:"+ ONPDTsql);
    									Statement stmt = conn.createStatement();
    									stmt.executeUpdate(ONPDTsql);
    								    conn.close();
    								}
    							}							
    
    
    						} catch (Exception e) {
    							// TODO: handle exception
    							e.printStackTrace();
    						}
    						
    						
    					 }//for
    				  }else{
    					  JOptionPane.showMessageDialog(null, "此路径下无db文件无法操作");//消息框
    				  }
    						
    				}
    					
    	});
    		
    
    		//删除全部
    		JButton btn_Button_DeleteAll = new JButton("全部删除");
    		btn_Button_DeleteAll.setBounds(317, 169, 99, 22);
    		frame.getContentPane().add(btn_Button_DeleteAll);
    		
    
    		//删除实现 DELETE FROM calls WHERE _id = 15
    		btn_Button_DeleteAll.addActionListener(new ActionListener(){
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    //				int n = JOptionPane.showConfirmDialog(null, "你确定要删除全部通话记录?", "删除全部通话记录",
    //						JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); 
    //				Object[] options ={ "确定", "取消" };  //自定义按钮上的文字
    //				int m = JOptionPane.showOptionDialog(null, "再次确认是否要删除全部?", "清空全部通话记录",
    //						JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); 
    				int res=JOptionPane.showConfirmDialog(null, "暂不实现,删除后会导致终端报错", "删除全部通话记录", JOptionPane.YES_NO_OPTION);
                    if(res==JOptionPane.YES_OPTION){
                    	//点击“是”后执行这个代码块
    //    				try {
    //    				 System.out.println("删除全部通讯记录");	
    //    				 Runtime.getRuntime().exec
    //    					("adb shell su -rm -r /data/data/com.android.providers.contacts/databases/calllog.db");
        				 JOptionPane.showMessageDialog(null, "请恢复出厂设置");//消息框
    //    				} catch (Exception e) {
    //    					// TODO: handle exception
    //    					
    //    				}   
                    }else{
                    	//点击“否”后执行这个代码块
                        System.out.println("您已取消删除操作");    
                        return;
                    }
    				
    			  
    			}
    			
    			
    		});
    		
    	
    		/*辅助设计*/
    		JLabel label_1 = new JLabel("进度显示:");
    		label_1.setBounds(14, 382, 95, 18);
    		frame.getContentPane().add(label_1);
    
    		JScrollPane scrollPane = new JScrollPane();
    		scrollPane.setBounds(14, 413, 462, 159);
    		frame.getContentPane().add(scrollPane);
    
    		JTextArea textArea = new JTextArea();
    		scrollPane.setViewportView(textArea);
    		//辅助操作
    		JLabel label_2 = new JLabel("辅助操作(对手机):");
    		label_2.setBounds(14, 269, 162, 29);
    		frame.getContentPane().add(label_2);
    		//root
    		JButton btn_Button_root = new JButton("root手机");
    		btn_Button_root.setBounds(14, 327, 119, 22);
    		frame.getContentPane().add(btn_Button_root);
    		//root功能
    		btn_Button_root.addActionListener(new ActionListener(){
    
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    				try {
    
    //					if(dev == null){
    //					 Runtime.getRuntime().exec("adb wait-for-device");
    	   				 Runtime.getRuntime().exec("adb root xxx.cn");//xxx是该产品的root密码
    	   				 Runtime.getRuntime().exec("adb remount");
    	   				 JOptionPane.showMessageDialog(null, "root成功!");//消息框
    //					 }
    	   				} catch (Exception e) {
    	   					// TODO: handle exception
    	   				JOptionPane.showMessageDialog(null, "请检查端口");//消息框
    	   				}   
    			}
    			
    		});
    		
    		JButton btn_Button_input = new JButton("导入db文件");
    		btn_Button_input.setBounds(292, 327, 119, 22);
    		frame.getContentPane().add(btn_Button_input);
    		//导入功能
    		btn_Button_input.addActionListener(new ActionListener(){
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    			int res=JOptionPane.showConfirmDialog(null, "请确认db文件路径选对了吗?", "必须root", JOptionPane.YES_NO_OPTION);
    			if(res==JOptionPane.YES_NO_OPTION){//选择确定执行此操作
    				try {
    					//先root
    					Runtime.getRuntime().exec("adb root xxx.cn");//xxx是该产品的root密码
    	   				Runtime.getRuntime().exec("adb remount");
    	   			    //需要传入文件路径
    					String strPath = jTextField.getText();				
    					System.out.println(strPath);//这个就是路径	
    					if(rdbtnDouble_YES.isSelected()==true){
    						System.out.println("选择是");
    						if(rdbtnType_public.isSelected()){
    							System.out.println("选择公网");//公网路径不变
    							Runtime.getRuntime().exec("adb push "+strPath+"calllog.db"
    			   						+" /data/data/com.android.providers.contacts/databases/");
    			   				JOptionPane.showMessageDialog(null, "导入成功,重启终端查看结果,如果重启后失败,请先检查路径,然后查看操作文档或联系邓盛誉,");//消息框
    			   				Runtime.getRuntime().exec("adb reboot");
    						}
    						if(rdbtnType_gota.isSelected()){
    							System.out.println("选择专网");
    							JOptionPane.showMessageDialog(null, "请确认安卓P双系统是否有专网?,请联系邓盛誉修改");//消息框
    						}
    						if(rdbtnType_echat.isSelected()){
    							System.out.println("选择echat"); 
    							Runtime.getRuntime().exec("adb push "+strPath+"zxts.gotasms.db"
    			   						+" /data/user/10/com.mcptt/databases/");
    			   				JOptionPane.showMessageDialog(null, "导入成功,重启终端查看结果,如果重启后失败,请先检查路径,然后查看操作文档或联系邓盛誉,");//消息框
    			   				Runtime.getRuntime().exec("adb reboot");
    						
    						}
    						if(rdbtnType_PDT.isSelected()){
    							System.out.println("选择PDT");
    							JOptionPane.showMessageDialog(null, "请确认安卓P双系统是否有PDT?,请联系邓盛誉修改");//消息框
    						}
    					}else{
    						System.out.println("选择否");
    						if(rdbtnType_public.isSelected()){
    							System.out.println("选择公网1");
    							Runtime.getRuntime().exec("adb push "+strPath+"calllog.db"
    			   						+" /data/data/com.android.providers.contacts/databases/");
    			   				JOptionPane.showMessageDialog(null, "导入成功,重启终端查看结果,如果重启后失败,请先检查路径,然后查看操作文档或联系邓盛誉,");//消息框
    			   				Runtime.getRuntime().exec("adb reboot");
    						}
    						if(rdbtnType_gota.isSelected()){
    							System.out.println("选择专网1");
    							Runtime.getRuntime().exec("adb push "+strPath+"zxts.gotasms.db"
    			   						+" /data/data/com.android.providers.contacts/databases/");
    			   				JOptionPane.showMessageDialog(null, "导入成功,重启终端查看结果,如果重启后失败,请先检查路径,然后查看操作文档或联系邓盛誉,");//消息框
    			   				Runtime.getRuntime().exec("adb reboot");
    						}
    						if(rdbtnType_echat.isSelected()){
    							System.out.println("选择echat1");
    							Runtime.getRuntime().exec("adb push "+strPath+"zxts.gotasms.db"
    			   						+" /data/user/10/com.mcptt/databases/");
    			   				JOptionPane.showMessageDialog(null, "导入成功,重启终端查看结果,如果重启后失败,请先检查路径,然后查看操作文档或联系邓盛誉,");//消息框
    			   				Runtime.getRuntime().exec("adb reboot");
    						}
    						if(rdbtnType_PDT.isSelected()){
    							System.out.println("选择PDT1");
    							Runtime.getRuntime().exec(
    			   						 "adb pull /data/data/com.xxx/databases/zxts.gotasms.db "+strPath);//xxx是该产品的包名
    							JOptionPane.showMessageDialog(null, "是否已复制到"+strPath+"根目录下,如果没有,请确认手机有无此zxts.gotasms.db文件");//消息框
    							
    						}
    					}
    	   				
    					
    	   				
    	   				
    	   				} catch (Exception e) {
    	   					// TODO: handle exception
    	   				
    	   				}   
    			 }else{
    				 JOptionPane.showMessageDialog(null, "不root无法执行此操作", "警告",JOptionPane.ERROR_MESSAGE);
    
    			 }
    		
    			}
    			
    		});
    		
    		JButton btn_Button_out = new JButton("导出db文件");
    		btn_Button_out.setBounds(145, 327, 131, 22);
    		frame.getContentPane().add(btn_Button_out);
    		
    		//导出功能
    		btn_Button_out.addActionListener(new ActionListener(){
    		 public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    			int res=JOptionPane.showConfirmDialog(null, "请确定无中文路径", "必须root", JOptionPane.YES_NO_OPTION);
    			if(res==JOptionPane.YES_NO_OPTION){//选择确定执行此操作
    				
    				try {
    					//先root
    					Runtime.getRuntime().exec("adb root xxx.cn");//xxx是该产品的root密码
    	   				Runtime.getRuntime().exec("adb remount");
    					//需要传入文件路径
    					 String strPath = jTextField.getText();				
    					 System.out.println(strPath);//这个就是路径	
    					 if(rdbtnDouble_YES.isSelected()==true){
    							System.out.println("选择是");
    							if(rdbtnType_public.isSelected()){
    								System.out.println("选择公网");//公网路径不变
    								Runtime.getRuntime().exec(
    				   						 "adb pull /data/data/com.android.providers.contacts/databases/calllog.db "+strPath);
    								JOptionPane.showMessageDialog(null, "请确认是否已导入到"+strPath+"根目录下,如果没有,请确认手机有无calllog.db文件");//消息框
    							}
    							if(rdbtnType_gota.isSelected()){
    								System.out.println("选择专网");
    								JOptionPane.showMessageDialog(null, "请确认安卓P双系统是否有专网?,请联系邓盛誉修改");//消息框
    							}
    							if(rdbtnType_echat.isSelected()){
    								System.out.println("选择echat"); 
    								Runtime.getRuntime().exec(
    				   						 "adb pull /data/user/10/com.mcptt/databases/zxts.gotasms.db "+strPath);
    							JOptionPane.showMessageDialog(null, "请确认是否已导入到"+strPath+"根目录下,如果没有,请确认手机有无zxts.gotasms.db文件");//消息框
    							}
    							if(rdbtnType_PDT.isSelected()){
    								System.out.println("选择PDT");
    								JOptionPane.showMessageDialog(null, "请确认安卓P双系统是否有PDT?,请联系邓盛誉修改");//消息框
    							}
    						}else{
    							System.out.println("选择否");
    							if(rdbtnType_public.isSelected()){
    								System.out.println("选择公网1");
    								Runtime.getRuntime().exec(
    				   						 "adb pull /data/data/com.android.providers.contacts/databases/calllog.db "+strPath);
    								JOptionPane.showMessageDialog(null, "请确认是否已导入到"+strPath+"根目录下,如果没有,请确认手机有无calllog.db文件");//消息框
    							}
    							if(rdbtnType_gota.isSelected()){
    								System.out.println("选择专网1");
    								Runtime.getRuntime().exec(
    				   						 "adb pull /data/data/com.xxx/databases/GoTaDb.db "+strPath);//xxx是该产品的包名
    								JOptionPane.showMessageDialog(null, "是否已复制到"+strPath+"根目录下,如果没有,请确认手机有无此GoTaDb.db文件");//消息框
    							}
    							if(rdbtnType_echat.isSelected()){
    								System.out.println("选择echat1");
    								Runtime.getRuntime().exec(
    				   						 "adb pull /data/data/com.mcptt/databases/zxts.gotasms.db "+strPath);
    							JOptionPane.showMessageDialog(null, "请确认是否已导入到"+strPath+"根目录下,如果没有,请确认手机有无zxts.gotasms.db文件");//消息框
    							}
    							if(rdbtnType_PDT.isSelected()){
    								System.out.println("选择PDT1");
    								Runtime.getRuntime().exec(
    				   						 "adb pull /data/data/com.xxx/databases/GoTaDb.db "+strPath);//xxx是该产品的包名
    								JOptionPane.showMessageDialog(null, "是否已复制到"+strPath+"根目录下,如果没有,请确认手机有无此GoTaDb.db文件");//消息框
    								
    							}
    						}
    	   				 
    	   				 
    	   				} catch (Exception e) {
    	   					// TODO: handle exception
    	   				 
    	   				}  
    				
    			}else{
    				//选择否弹出警告
    				JOptionPane.showMessageDialog(null, "不root或中文路径无法执行此操作", "盛誉警告",JOptionPane.ERROR_MESSAGE);
    			 }
    
    		 }
    			
    		});
    		
    
    	 	
    	}
    }
    

    展开全文
  • PackageManagerService及APK安装流程剖析

    千次阅读 2019-03-27 10:19:32
    PackageManagerService及APK安装流程剖析 目录 PackageManagerService及APK安装流程剖析 1 概述 1.1 PMS介绍 1.2 PMS启动 2 APK分析 2.1 Apk包组成 2.2 Apk文件分析 3 PMS安装流程 3.1介绍 3.2 流程...

    PackageManagerService及APK安装流程剖析

     

    目录

    PackageManagerService及APK安装流程剖析

    1 概述

    1.1 PMS介绍

    1.2 PMS启动

    2 APK分析

    2.1 Apk包组成

    2.2  Apk文件分析

    3 PMS安装流程

    3.1介绍

    3.2 流程分析


     

    1 概述

    1.1 PMS介绍

    PMS是PackageManagerService服务简称,常驻在SystemServer进程内,负责Android系统安装包信息,安装全流程管理。其在SystemServer中被启动,代码如下,是Android系统核心服务之一。

    1.2 PMS启动

    PMS作为Android系统的核心服务之一,负责的核心功能包括但不限于:包信息管理,包解析,权限管理,多包统一管理,Apk安装全流程管理,其它系统服务交互管理。可见其功能复杂,需要精心的架构设计才能完成其功能。

    首先来看下基于Android4.4的PMS静态架构图,帮助我们理解后面的内容:

     

    接下来重点来分析下PMS的启动方法,简要来看下其在启动时操作的业务,如下

    继续来看它的新建方法,这个方法较长,我们分解来看,主要做了几个事情

    1. 新建mSettings对象,辅助Package管理
    2. 已安装程序扫描分析
    3. 安装包APk路径扫描机安装
    4. 通过mSettings回写系统app现状,创建packages.xml文件。

    这里来看下,mSettings的新建

    这个对象负责package包的日常管理,是及其重要的,其类为Settings,这里先不展开。

    接下来PMS分析已安装APK的现状,如下:‘

    这一步主要负责已安装APP现状分析(基于持久化文件),构建内存对象,核心是PMS中的mPackages对象。

    PMS新建方法中的第三部是非常核心的,负责APK包的扫描分析,包括系统预装路径下APK及应用目录下的APK,其中还涉及到APK解析及安装,如下

    跟踪进入方法scanDirLI方法来看如下:

    List目录,并对apk文件执行scanPackageLI方法,继续深入此方法,关注核心点如下:

    到此为止调用scanPackageLI方法完成apk解析及信息提取,有兴趣的可以深入进去。通过这步PMS的mPackages方法已经填充完毕,接下来来看PMS构建方法的第四步,更新持久化信息packages.xml。

    PMS通过mSettings.writeLPr()达到目标,如下

    继续跟踪,如下

    可以看到通过fs最终更新packages.xml文件。

    至此PMS的初始化方法,完成了Android系统APP包信息的统一,安装,权限收集等,以待其它系统服务的调用。

    2 APK分析

    2.1 Apk包组成

    我们在分析安装流程前,先显示来看下android安装包即APK的组成。APK是AndroidPackage的缩写,即Android安装包(apk)。APK是类似Symbian Sis或Sisx的文件格式。通过将APK文件直接传到Android模拟器或Android手机中执行即可安装。

    apk文件和sis一样,把android sdk编译的工程打包成一个安装程序文件,格式为apk APK文件其实是zip格式,但后缀名被修改为apk,通过UnZip解压后,可以看到Dex文件,DexDalvikVM executes的简称,即Android Dalvik执行程序,并非Java ME字节码而是Dalvik字节码。Android在运行一个程序时首先需要UnZip,然后类似Symbian那样直接,和Windows Mobile中的PE文件有区别。

    我们通过分析一个举列子,来看下具体的APK文件。

    得到结果如下

    剩下的META-INF主要是对此apk文件的加密信息。

    2.2  Apk文件分析

     了解了APK文件的实质以及内容后,可以更进一步,上文提到classes.dex是apk可执行文件的二进制码,被davilk解析执行,这里可以通过一些开源的反编译工具查看内容,但受限于混淆技术,可看到的东西其实不多。

      此时直接打开AndroidMannifest.XML文件,发现无法查看,这其实是Android在apk打包过程中对xml进行了压缩导致,可以通过源代码下的aapt工具来查看,执行如下命令../prebuilts/sdk/tools/linux/aapt d xmlstrings pmcszj.apk AndroidManifest.xml

    同理,我们还可以查看此apk归档文件申请的权限,通过执行下面命令../prebuilts/sdk/tools/linux/aapt d permissions pmcszj.apk

    可以看到此示列程序申请了wifi,读写rom磁盘权限等。看过1.2PMS启动的分析就会明白,此apk安装后,其权限信息已经被提取记录到PMS中。

    下面来看下apk归档文件的加密信息,METE_INFO下面有三个文件

    1  MENIFEST.MF :保存对所有文件的sha(安全哈希)的base24编码 (可以直接打开,我用的notepad++)

    2  CERT.SF : 对MENIFEST.MF中的每一项进行sha(安全哈希) 并且通过apk签名的私钥进行运算后的base64编码 (可以直接打开,我用的notepad++)

    3 CERT.RSA : 证书文件,封装对apk数字签名对应公钥信息,采用RSA算法。

    简单点来说其原理是MENIFEST.MF对普通的类和资源文件进行校验,CERT.SF 加上 CERT.RSA 校验MENIFEST.MF . 这样就保证不能伪造他人来发布apk。

    可以通过keytool -printcert -file test/META-INF/SCREENTE.RSA查看示列程序的公钥信息,如下

     

    至此初步完成了对apk文件的分析,其本质只是一个符合Android平台开发规范的协议归档文件。

    3 PMS安装流程

    3.1介绍

    Android平台下安装apk程序,有多重途径,这里以adb shell install开始做入口来分析,其它方式大同小异,查看frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java

    文件,存在入口如下:

    这里可以看到通过Binder转接,实质是通过PMS来实现安装,其调用时序如下参考如下:

     

    3.2 流程分析

    下面,逐步来分析apk安装流程

    1 首先分析installPackageWithVerificationAndEncryption方法

    在这里做了基本的检查,原封不动对参数构建InstallParams对象,并发送INIT_COPY信息来处理。

    这里关注下类InstallParams,如下:

     

    可以看出,此类可抽象理解为对安装参数处理,其内部封装了一些核心方法来触发安装,是PMS中重要的辅助类。

    2 基于第一点分析,通过内部handler发送INIT_COPY命令处理封装信息:

    这里添加param参数到list中,接着触发MCS_BOUND命令,如下

    由上面知道mPendingInstalls内至少有一个对象,接着执行params的startCopy方法,接着来看

     

    这里可以看到,此方法基本主要分两步:

    • handleStartCopy:处理apk的拷贝
    • handleReturnCode:处理拷贝后的后续安装

    下面基于这两个方法,分段来分析。

    3 接着上面分析,这里分析handleStartCopy方法,其过程较长,这里关注核心的几个点

    通过params传入的参数,来新建packagefile,笔者这里测试信息参考如下:

    /data/app/vmdl-734788364.tmp

    然后通过mContainerService服务来获取package的基本信息,这里可以看到:

    这主要是对位置以及packagename的解析。这里重点是location参数的解析。Android系统接下来还要对location进行核实(通过自定义策略支持),如下

    这里不展开,在新安装的app,不会改变packagelite中的取值。

    接下来基于InstallParams来构建FileInstallArgs对象,如下

     

    之前在上文提到了InstallParams类的作用,其主要负责安装参数封装及安装触发,这里也来看下FileInstallArgs的抽象意义

     

    可以看到FileInstallArgs主要负责文件的复制,以及安装生命周期回调。持有Apk相关核心参数。

    继续来看handleStartCopy方法

    这里有个if else分支,对于新安装程序(不许要VerifierPackag),直接调用args的copyapk来处理,而次args对象就是刚刚提到的FileInstallArgs对象,接着来看

     

    这里主要是创建temp文件及确定路径,笔者测试时列子中,其codefilename:path=/data/app/vmdl-734788364.tmp

    接下来看

    可以看到这里主要做了两件事,就是从adb临时目录(file:///data/local/tmp/da021051-c596-4422-903a-0c6600320e2d.apk)下拷贝资源到刚刚创建的temp文件主要是code及lib拷贝

    至此已经完成了从adb临时文件到temp拷贝,并确定了基本的一些路径参数

     

    4下面接着第三步来分析handleReturnCode函数

    这里可以看到,直接调用PMS下的processPendingInstall 方法来处理,其中入参包括mArgs(InstallParams)

    这里可以看到在PMS中post的了一个线程来处理接下来工作,可以预见其比较耗时,这里忽略安装前的参数检查,直接来看installPackageLI方法

    这里面画箭头的地方,主要做了两件事,基于传递进入的InstallArgs参数

    1 通过PackageParser解析apk信息,提取出PackageParser.Package对象,后面有机会介绍这个是apk类在内存中对应的重要的对象。

    2 通过package及flags参数来提取mete-inf中的rsa信息,这个在第二章节中已经提到,这在后面权限验证会用到。

    接下来继续分析

    可以看到这里直接调用到了installNewPackageLI方法,这里继续来看

    此方法中主要做了两件事情,调用scanPackageLI来浏览梳理apk信息,之后调用updateSettingsLI方法,根据方法名我猜应该是更新本地化配置(package.xml),这个在第一章分析过。

     

    5 接下来重点分析下scanPackageLI方法

    这里分段来看。首先,打开之前拷贝的temp文件

    接下来更新共享库

    这里基于传入的pkg来更新msetting中的对象(会新建,传入其list中,这里不展开)

    解析最新的pkg信息后,就是检查rsa文件等,如下

    下面,创建app的安装目录,如下

    其实还有一些列资源文件lib文件拷贝的。

    分析到这里即便是解析及拷贝,下面就是基于全新的pkg来更新内存中的对象,针对apk中声明的Andorid四大组件,这为AMS管理组件声明周期提供了基础,这里代码太多,只给出activity的提取及示列

    笔者做测试的列子,提取到的信息日志如下

    
    V/PackageManager( 7148):   activity com.waps.OffersWebView:
    V/PackageManager( 7148):     Class=com.waps.OffersWebView
    V/PackageManager( 7148):   activity ps.wb.music20130822001587.MyAdView:
    V/PackageManager( 7148):     Class=ps.wb.music20130822001587.MyAdView
    V/PackageManager( 7148):   activity ps.wb.music20130822001587.MusicSet:
    V/PackageManager( 7148):     Class=ps.wb.music20130822001587.MusicSet
    V/PackageManager( 7148):   activity ps.wb.music20130822001587.Index:
    V/PackageManager( 7148):     Class=ps.wb.music20130822001587.Index
    V/PackageManager( 7148):     IntentFilter:
    V/PackageManager( 7148):       Action: "android.intent.action.MAIN"
    V/PackageManager( 7148):       Category: "android.intent.category.LAUNCHER"
    V/PackageManager( 7148):   activity ps.md.music.InputFile:
    V/PackageManager( 7148):     Class=ps.md.music.InputFile

    至此基本分析完了scanPackageLI方法,可以看到其实安装最核心的方法,涉及到temp文件解析,权限检查,安装目录新建及复制,以及内存对象构建等。

    6 接下来继续分析,我们在第四步分析时提到过最终还会执行updateSettingsLI方法,这里来简单看下

    其实这个方法很简单,就是针对上面安装apk后的信息,执行mSettings.writeLPr()方法来更新package.xml配置文件,以便于下次Android系统启动,从此文件恢复核心的内存对象。

    至此基本分析完了apk的安装流程,由于涉及到信息量太多,这里采用抓主干方式,后期有时间可以针对一些细节来展开。

    展开全文
  • ⑥创建META目录并向该目录下添加一些文本文件,如apkcerts.txt(描述apk文件用到的认证证书),misc_info.txt(描述Flash内存的块大小以及boot、recovery、system、userdata等分区的大小信息)。 ⑦使用保留连接选项...

    满意答案

    02ae427d08e371d7e90d5b995e828d6d.png

    dwhu8l2gph9

    推荐于 2017.10.09

    02ae427d08e371d7e90d5b995e828d6d.png

    采纳率:48%    等级:13

    已帮助:5931人

    通过分析update.zip包在具体Android系统升级的过程,来理解Android系统中Recovery模式服务的工作原理。

    我们先从update.zip包的制作开始,然后是Android系统的启动模式分析,Recovery工作原理,如何从我们上层开始选择system update到重启到Recovery服务,以及在Recovery服务中具体怎样处理update.zip包升级的,我们的安装脚本updater-script怎样被解析并执行的等一系列问题。分析过程中所用的Android源码是gingerbread0919(tcc88xx开发板标配的),测试开发板是tcc88xx。

    一、 update.zip包的目录结构

    |----boot.img

    |----system/

    |----recovery/

    `|----recovery-from-boot.p

    `|----etc/

    `|----install-recovery.sh

    |---META-INF/

    `|CERT.RSA

    `|CERT.SF

    `|MANIFEST.MF

    `|----com/

    `|----google/

    `|----android/

    `|----update-binary

    `|----updater-script

    `|----android/

    `|----metadata

    二、 update.zip包目录结构详解

    以上是我们用命令make otapackage 制作的update.zip包的标准目录结构。

    1、boot.img是更新boot分区所需要的文件。这个boot.img主要包括kernel+ramdisk。

    2、system/目录的内容在升级后会放在系统的system分区。主要用来更新系统的一些应用或则应用会用到的一些库等等。可以将Android源码编译out/target/product/tcc8800/system/中的所有文件拷贝到这个目录来代替。

    3、recovery/目录中的recovery-from-boot.p是boot.img和recovery.img的补丁(patch),主要用来更新recovery分区,其中etc/目录下的install-recovery.sh是更新脚本。

    4、update-binary是一个二进制文件,相当于一个脚本解释器,能够识别updater-script中描述的操作。该文件在Android源码编译后out/target/product/tcc8800/system bin/updater生成,可将updater重命名为update-binary得到。

    该文件在具体的更新包中的名字由源码中bootable/recovery/install.c中的宏ASSUMED_UPDATE_BINARY_NAME的值而定。

    5、updater-script:此文件是一个脚本文件,具体描述了更新过程。我们可以根据具体情况编写该脚本来适应我们的具体需求。该文件的命名由源码中bootable/recovery/updater/updater.c文件中的宏SCRIPT_NAME的值而定。

    6、 metadata文件是描述设备信息及环境变量的元数据。主要包括一些编译选项,签名公钥,时间戳以及设备型号等。

    7、我们还可以在包中添加userdata目录,来更新系统中的用户数据部分。这部分内容在更新后会存放在系统的/data目录下。

    8、update.zip包的签名:update.zip更新包在制作完成后需要对其签名,否则在升级时会出现认证失败的错误提示。而且签名要使用和目标板一致的加密公钥。加密公钥及加密需要的三个文件在Android源码编译后生成的具体路径为:

    out/host/linux-x86/framework/signapk.jar

    build/target/product/security/testkey.x509.pem

    build/target/product/security/testkey.pk8 。

    我们用命令make otapackage制作生成的update.zip包是已签过名的,如果自己做update.zip包时必须手动对其签名。

    具体的加密方法:$ java –jar gingerbread/out/host/linux/framework/signapk.jar –w gingerbread/build/target/product/security/testkey.x509.pem gingerbread/build/target/product/security/testkey.pk8 update.zip update_signed.zip

    以上命令在update.zip包所在的路径下执行,其中signapk.jar testkey.x509.pem以及testkey.pk8文件的引用使用绝对路径。update.zip 是我们已经打好的包,update_signed.zip包是命令执行完生成的已经签过名的包。

    9、MANIFEST.MF:这个manifest文件定义了与包的组成结构相关的数据。类似Android应用的mainfest.xml文件。

    10、CERT.RSA:与签名文件相关联的签名程序块文件,它存储了用于签名JAR文件的公共签名。

    11、CERT.SF:这是JAR文件的签名文件,其中前缀CERT代表签名者。

    另外,在具体升级时,对update.zip包检查时大致会分三步:①检验SF文件与RSA文件是否匹配。②检验MANIFEST.MF与签名文件中的digest是否一致。③检验包中的文件与MANIFEST中所描述的是否一致。

    三、 Android升级包update.zip的生成过程分析

    1) 对于update.zip包的制作有两种方式,即手动制作和命令生成。

    第一种手动制作:即按照update.zip的目录结构手动创建我们需要的目录。然后将对应的文件拷贝到相应的目录下,比如我们向系统中新加一个应用程序。可以将新增的应用拷贝到我们新建的update/system/app/下(system目录是事先拷贝编译源码后生成的system目录),打包并签名后,拷贝到SD卡就可以使用了。这种方式在实际的tcc8800开发板中未测试成功。签名部分未通过,可能与具体的开发板相关。

    第二种制作方式:命令制作。Android源码系统中为我们提供了制作update.zip刷机包的命令,即make otapackage。该命令在编译源码完成后并在源码根目录下执行。 具体操作方式:在源码根目录下执行

    ①$ . build/envsetup.sh。

    ②$ lunch 然后选择你需要的配置(如17)。

    ③$ make otapackage。

    在编译完源码后最好再执行一遍上面的①、②步防止执行③时出现未找到对应规则的错误提示。命令执行完成后生成的升级包所在位置在out/target/product/full_tcc8800_evm_target_files-eng.mumu.20120309.111059.zip将这个包重新命名为update.zip,并拷贝到SD卡中即可使用。

    这种方式(即完全升级)在tcc8800开发板中已测试成功。

    2) 使用make otapackage命令生成update.zip的过程分析。

    在源码根目录下执行make otapackage命令生成update.zip包主要分为两步,第一步是根据Makefile执行编译生成一个update原包(zip格式)。第二步是运行一个python脚本,并以上一步准备的zip包作为输入,最终生成我们需要的升级包。下面进一步分析这两个过程。

    第一步:编译Makefile。对应的Makefile文件所在位置:build/core/Makefile。从该文件的884行(tcc8800,gingerbread0919)开始会生成一个zip包,这个包最后会用来制作OTA package 或者filesystem image。先将这部分的对应的Makefile贴出来如下:

    [python] view plaincopyprint?

    # -----------------------------------------------------------------

    # A zip of the directories that map to the target filesystem.

    # This zip can be used to create an OTA package or filesystem image

    # as a post-build step.

    #

    根据上面的Makefile可以分析这个包的生成过程:

    首先创建一个root_zip根目录,并依次在此目录下创建所需要的如下其他目录

    ①创建RECOVERY目录,并填充该目录的内容,包括kernel的镜像和recovery根文件系统的镜像。此目录最终用于生成recovery.img。

    ②创建并填充BOOT目录。包含kernel和cmdline以及pagesize大小等,该目录最终用来生成boot.img。

    ③向SYSTEM目录填充system image。

    ④向DATA填充data image。

    ⑤用于生成OTA package包所需要的额外的内容。主要包括一些bin命令。

    ⑥创建META目录并向该目录下添加一些文本文件,如apkcerts.txt(描述apk文件用到的认证证书),misc_info.txt(描述Flash内存的块大小以及boot、recovery、system、userdata等分区的大小信息)。

    ⑦使用保留连接选项压缩我们在上面获得的root_zip目录。

    ⑧使用fs_config(build/tools/fs_config)配置上面的zip包内所有的系统文件(system/下各目录、文件)的权限属主等信息。fs_config包含了一个头文件#include“private/android_filesystem_config.h”。在这个头文件中以硬编码的方式设定了system目录下各文件的权限、属主。执行完配置后会将配置后的信息以文本方式输出 到META/filesystem_config.txt中。并再一次zip压缩成我们最终需要的原始包。

    第二步:上面的zip包只是一个编译过程中生成的原始包。这个原始zip包在实际的编译过程中有两个作用,一是用来生成OTA update升级包,二是用来生成系统镜像。在编译过程中若生成OTA update升级包时会调用(具体位置在Makefile的1037行到1058行)一个名为ota_from_target_files的python脚本,位置在/build/tools/releasetools/ota_from_target_files。这个脚本的作用是以第一步生成的zip原始包作为输入,最终生成可用的OTA升级zip包。

    二 下面我们分析ota_from_target_files这个python脚本是怎样生成最终zip包的。先讲这个脚本的代码贴出来如下:

    [python] view plaincopyprint?

    import sys

    if sys.hexversion < 0x02040000:

    print >> sys.stderr, "Python 2.4 or newer is required."

    sys.exit(1)

    主函数main是python的入口函数,我们从main函数开始看,大概看一下main函数(脚本最后)里的流程就能知道脚本的执行过程了。

    ① 在main函数的开头,首先将用户设定的option选项存入OPTIONS变量中,它是一个python中的类。紧接着判断有没有额外的脚本,如果有就读入到OPTIONS变量中。

    ② 解压缩输入的zip包,即我们在上文生成的原始zip包。然后判断是否用到device-specific extensions(设备扩展)如果用到,随即读入到OPTIONS变量中。

    ③ 判断是否签名,然后判断是否有新内容的增量源,有的话就解压该增量源包放入一个临时变量中(source_zip)。自此,所有的准备工作已完毕,随即会调用该 脚本中最主要的函数WriteFullOTAPackage(input_zip,output_zip)

    ④ WriteFullOTAPackage函数的处理过程是先获得脚本的生成器。默认格式是edify。然后获得metadata元数据,此数据来至于Android的一些环境变量。然后获得设备配置参数比如api函数的版本。然后判断是否忽略时间戳。

    ⑤ WriteFullOTAPackage函数做完准备工作后就开始生成升级用的脚本文件(updater-script)了。生成脚本文件后将上一步获得的metadata元数据写入到输出包out_zip。

    ⑥至此一个完整的update.zip升级包就生成了。生成位置在:out/target/product/tcc8800/full_tcc8800_evm-ota-eng.mumu.20120315.155326.zip。将升级包拷贝到SD卡中就可以用来升级了。

    四、 Android OTA增量包update.zip的生成

    在上面的过程中生成的update.zip升级包是全部系统的升级包。大小有80M多。这对手机用户来说,用来升级的流量是很大的。而且在实际升级中,我们只希望能够升级我们改变的那部分内容。这就需要使用增量包来升级。生成增量包的过程也需要上文中提到的ota_from_target_files.py的参与。

    下面是制作update.zip增量包的过程。

    ① 在源码根目录下依次执行下列命令

    $ . build/envsetup.sh

    $ lunch 选择17

    $ make

    $ make otapackage

    执行上面的命令后会在out/target/product/tcc8800/下生成我们第一个系统升级包。我们先将其命名为A.zip

    ② 在源码中修改我们需要改变的部分,比如修改内核配置,增加新的驱动等等。修改后再一次执行上面的命令。就会生成第二个我们修改后生成的update.zip升级包。将 其命名为B.zip。

    ③ 在上文中我们看了ota_from_target_files.py脚本的使用帮助,其中选项-i就是用来生成差分增量包的。使用方法是以上面的A.zip 和B.zip包作为输入,以update.zip包作 为输出。生成的update.zip就是我们最后需要的增量包。

    具体使用方式是:将上述两个包拷贝到源码根目录下,然后执行下面的命令。

    $ ./build/tools/releasetools/ota_from_target_files -i A.zip B.zip update.zip。

    在执行上述命令时会出现未找到recovery_api_version的错误。原因是在执行上面的脚本时如果使用选项i则会调用WriteIncrementalOTAPackage会从A包和B包中的META目录下搜索misc_info.txt来读取recovery_api_version的值。但是在执行make otapackage命令时生成的update.zip包中没有这个目录更没有这个文档。

    此时我们就需要使用执行make otapackage生成的原始的zip包。这个包的位置在out/target/product/tcc8800/obj/PACKAGING/target_files_intermediates/目录下,它是在用命令make otapackage之后的中间生产物,是最原始的升级包。我们将两次编译的生成的包分别重命名为A.zip和B.zip,并拷贝到SD卡根目录下重复执行上面的命令:

    $ ./build/tools/releasetools/ota_form_target_files -i A.zip B.zip update.zip。

    在上述命令即将执行完毕时,在device/telechips/common/releasetools.py会调用IncrementalOTA_InstallEnd,在这个函数中读取包中的RADIO/bootloader.img。

    而包中是没有这个目录和bootloader.img的。所以执行失败,未能生成对应的update.zip。可能与我们未修改bootloader(升级firmware)有关。此问题在下一篇博客已经解决。

    制作增量包失败的原因,以及解决方案。

    Android系统Recovery工作原理之使用update.zip升级过程分析(二)---update.zip差分包问题的解决

    在上一篇末尾提到的生成差分包时出现的问题,现已解决,由于最近比较忙,相隔的时间也比较长,所以单列一个篇幅提示大家。这个问题居然是源码中的问题,可能你已经制作成功了,不过我的这个问题确实是源码中的一个问题,不知道是不是一个bug,下文会具体分析!

    一、生成OTA增量包失败的解决方案

    在上一篇中末尾使用ota_from_target_files脚本制作update.zip增量包时失败,我们先将出现的错误贴出来。

    在执行这个脚本的最后读取input_zip中RADIO/bootloader.img时出现错误,显示DeviceSpecifiParams这个对象中没有input_zip属性。

    我们先从脚本中出现错误的调用函数中开始查找。出现错误的调用地方是在函WriteIncrementalOTAPackage(443行)中的device_specific.IncrementalOTA_InstallEnd(),其位于WriteIncrementalOTAPackage()中的末尾。进一步跟踪源码发现,这是一个回调函数,他的具体执行方法位于源码中/device/telechips/common/releasetools.py脚本中的IncrementalOTA_InstallEnd()函数。下面就分析这个函数的作用。

    releasetools.py脚本中的两个函数FullOTA_InstallEnd()和IncrementalOTA_InstallEnd()的作用都是从输入包中读取RADIO/下的bootloader.img文件写到输出包中,同时生成安装bootloader.img时执行脚本的那部分命令。只不过一个是直接将输入包中的bootloader.img镜像写到输出包中,一个是先比较target_zip和source_zip中的bootloader.img是否不同(使用选项-i生成差分包时),然后将新的镜像写入输出包中。下面先将这个函数(位于/device/telechips/common/releasetools.py)的具体实现贴出来:

    我们的实际情况是,在用命令make otapackage时生成的包中是没有这个RADIO目录下的bootloader.img镜像文件(因为这部分更新已被屏蔽掉了)。但是这个函数中对于从包中未读取到bootloader.img文件的情况是有错误处理的,即返回。所以我们要从 出现的实际错误中寻找问题的原由。

    真正出现错误的地方是:

    target_bootloader=info.input_zip.read(“RADIO/bootloader.img”)。

    出现错误的原因是:AttributeError:‘DeviceSpecificParams’object has no attribute ‘input_zip’,提示我们DeviceSpecificParams对象没有input_zip这个属性。

    二、updater-script脚本执行流程分析:

    先看一下在测试过程中用命令make otapackage生成的升级脚本如下:

    [python] view plaincopyprint?

    assert(!less_than_int(1331176658, getprop("ro.build.date.utc")));

    assert(getprop("ro.product.device") == "tcc8800" ||

    下面分析下这个脚本的执行过程:

    ①比较时间戳:如果升级包较旧则终止脚本的执行。

    ②匹配设备信息:如果和当前的设备信息不一致,则停止脚本的执行。

    ③显示进度条:如果以上两步匹配则开始显示升级进度条。

    ④格式化system分区并挂载。

    ⑤提取包中的recovery以及system目录下的内容到系统的/system下。

    ⑥为/system/bin/下的命令文件建立符号连接。

    ⑦设置/system/下目录以及文件的属性。

    ⑧将包中的boot.img提取到/tmp/boot.img。

    ⑨将/tmp/boot.img镜像文件写入到boot分区。

    ⑩完成后卸载/system。

    三、总结

    以上的九篇着重分析了Android系统中Recovery模式中的一种,即我们做好的update.zip包在系统更新时所走过的流程。其核心部分就是Recovery服务的工作原理。其他两种FACTORY RESET、ENCRYPTED FILE SYSTEM ENABLE/DISABLE与OTA INSTALL是相通的。重点是要理解Recovery服务的工作原理。另外详细分析其升级过程,对于我们在实际升级时,可以根据我们的需要做出相应的修改。

    11分享举报

    展开全文
  • vivo手机安装应用自动输入密码

    千次阅读 2018-06-01 07:27:19
    公司买了台vivo的x21 UDA的测试机,在使用Android Studio安装到手机的时候每次都必须输入密码,还没办法关闭,vivo手机不能root 不能刷机,实在太恶心了。所以想到用AccessibilityService写个辅助功能,在每次安装的...
  • DevUtils 是一个 Android 工具库, 主要根据不同功能模块,封装快捷使用的工具类及 API 方法调用。 该项目尽可能的便于开发人员,快捷、快速开发安全可靠的项目,以及内置部分常用的资源文件,如color.xml、(toast) ...
  • 手机系统内存是一种随机存取存储器...分类手机内存一般分为:RAM和ROMRAM运行内存通常是作为操作系统或其他正在运行程序的临时存储介质,也称作系统内存。就好比计算机中的内存条,如果内存条容量越大,计算机就有...
  • DevUtils 是一个 Android 工具库, 主要根据不同功能模块,封装快捷使用的工具类及 API 方法调用。 该项目尽可能的便于开发人员,快捷、快速开发安全可靠的项目,以及内置部分常用的资源文件,如color.xml、(toast) ...
  • 作者博客http://www.jianshu.com/u/abc8086489c7文章目录前言基础知识dexodexsmalirom层应用分析odex与apk框架文件逆向工具箱baksm...
  • 制作三星I9088 刷机ROM的实践(三)

    千次阅读 2010-12-23 11:58:00
    AmsComposer.apk(动感短信)、BuddiesNow.* (新闻)、mobserver.apk手机营业厅)、CUHotline.apk(联通客服)、Vissage.apk(乐媒)、wo3g.apk(沃3G)。   其它的就靠自己去探索了。千万记住一点: 胆大...
  • 当前工作中经常用到一些自动化脚本处理文体,搜罗一些常用的adb命令,分享给大家,一起成长。 #!/usr/bin/evn python ...adb 工具类 """ import os import platform import re import time import util.
  • 使用 macOS 为安卓刷机

    千次阅读 2020-02-23 14:53:22
    相较于window端各种版本层出不穷的安卓刷机教程,在OS X上刷机的教程不仅资源较少,而且多数都年代久远。本帖整合了最新(2020-2),较简单的macOS刷机教程,希望可以帮助到各位。本帖所有操作都基于最新的 macOS ...
  • 在小米 三星 索尼 手机 :图标上显示数字(未读消息数):这部分代码,是从QQ5.0.apk中找的。 小米已经測试通过了。 三星和索尼的,因为没有对应的手机。没有測试,有的,可能改动一下代码(推断是什么手机的代码...
  •  目前大多数厂商的手机都具备的切换主题的功能,以一个apk的形式将所有资源打包,切换主题时会动态提醒每个应用的资源管理器,将需要使用的资源信息添加进去并重新刷新界面,使其能够使用已经替换过的资源,从而...
  • 安卓手机Recovery概述和原理分析

    千次阅读 2019-04-30 10:35:11
    1、Recovery是用户想要刷机的过程中经常会遇到的一个词。那么什么是Recovery?Recovery模式又是什么意思?手机怎么进入Recovery模式? 2、Recovery的字面意思是恢复、复原。对于手机来说,Recovery就是安卓的手机...
  • 要下载:解锁工具+线刷工具 先解锁(配合手机上开发者模式),再线刷。 前人的链接:点此 线刷包解压后,还有一层目录,要选择底层中, 要勾选下面的全部删除,由于后续还要装面具,选全部删除即可。 ...
  • 在小米 三星 索尼 手机 :图标上显示数字(未读消息数):这部分代码,是从QQ5.0.apk中找的。 小米已经测试通过了, 三星和索尼的,由于没有相应的手机,没有测试,有的,可能修改一下代码(判断是什么手机的代码...
  • 首先写一个入口activiy,再在主的手机软件展示的activity里面写逻辑文件,需要依赖到工具类AppInfos.java来获取到所有的需要展示的特征,通过集合来返回数据到activity进行展示,先创建一个bean初始化一个app所需要...
  • android安卓源码海量项目合集打包-1

    万次阅读 多人点赞 2019-06-11 16:16:24
    │ │ 该Demo主要实现了读取手机中的联系人,并且以字母索引表的形式显示,类似于微信中的手机通讯一样的字母索引表.rar │ │ 通讯录按首字母排列,关键字查找.rar │ │ 鱼眼索引控件详解之二 —— 快速索引雏形...
  • 图标配置非常简单,Android平台没有对图标进行限制,按照建议的分辨率配置即可,可以有透明区域,也可以是圆角图标,可能有些特殊ROM对图标有所要求,提交应用市场时注意看是否有要求说明。但是,iOS平台对图标却...
  • 一、【安卓手机自适应draw9patch不失真背景】实际问题 前一段时间,去长江玩了一趟,拍了很多照片,不过都是手机拍的,正常尺寸都是看不清楚老婆的脸蛋,就不自在的开始放大放小,可是一定程度图片就失真了。不知道...
  • 然后下面是一个RelativeLayout,包含了两个TextView,分别用来显示手机和SD卡可用容量。最后是一个FramLayout,包含一个ListView,用来显示已安装的app的具体信息,还有一个TextView,由来显示用户/系统应用个数。 ...
  • 此外,AndroidO也带来了更加智能化的信息填充系统,未来不仅仅是Chrome中的表格信息,App中的信息也可以自动填充了,而新增的"智能文本选择”,可用于更加效率的复制和粘贴。 Vitals(核心功能)主要是谷歌在系统上更...
  • )init进程解析init.rc配置文件,当解析Zygote进程的Service语句时,根据参数构造一个Service对象,根据选项域的内容填充Service对象,最后将Service对象加入Service链表中。   b )init进程解析init.rc配置文件,...
  • * 当手机位置发生改变的时候 调用的方法 */ @Override public void onLocationChanged(Location location) { String latitude ="latitude "+ location.getLatitude(); // weidu String ...
  • Android面试必备知识点总结

    千次阅读 2019-04-11 17:23:57
    本文原文(MarkDown)链接:...文章目录Android的系统架构是怎么样的?Android手机启动过程BootloaderKernelinitZygote启动系统服务引导完成Activity启动过程如何退出APPAndroid四大组件ActivityActi...

空空如也

空空如也

1 2 3 4 5 ... 17
收藏数 334
精华内容 133
关键字:

智能手机rom填充工具apk

友情链接: AFerMCU_NPK.rar